第二天-Perl 6: 符号, 变量和容器
今天,我们将学习什么是容器,以及如何使用它们,但是首先,我希望你暂时忘记你对 Perl 6 的符号和变量的所有知识或怀疑,特别是如果你来自 Perl 5 的背景。 忘记一切。
把钱拿出来
在 Perl 6 中,变量以 $
符号为前缀,用绑定运算符(:=
)赋值。 像这样:
my $foo := 42;
say "The value is $foo"; # OUTPUT: «The value is 42
»
my $ordered-things := <foo bar ber>;
my $named-things := %(:42foo, :bar<ber>);
say "$named-things<foo> bottles of $ordered-things[2] on the wall";
# OUTPUT: «42 bottles of ber on the wall
»
.say for $ordered-things; # OUTPUT: «foo
bar
ber
»
.say for $named-things; # OUTPUT: «bar => ber
foo => 42
»
了解这一点,你可以写出各种各样的程序,所以如果你开始觉得有太多的东西需要学习,记住你不需要一次学习所有东西。
我们祝你有一个愉快的列表圣诞
让我们试着用我们的变量做更多的事情。 想要更改列表中的值并不罕见。 到目前为止我们的表现如何呢?
my $list := (1, 2, 3);
$list[0] := 100;
# OUTPUT: «Cannot use bind operator with this left-hand side […] »
1 := 100;
# OUTPUT: «Cannot use bind operator with this left-hand side […] »
my $list := (my $, my $, my $);
$list[0] := 100;
say $list; # OUTPUT: «(100 (Any) (Any))
»
my $list := (my $, my $, my $);
$list[0] := 100;
$list[0] := 200;
# OUTPUT: «Cannot use bind operator with this left-hand side […] »
我们需要一个更好的工具。
That's Your Assignment
my $list := (my $ = 1, my $ = 2, my $ = 3);
$list[0] = 100;
$list[0] = 200;
say $list;
# OUTPUT: «(200 2 3)
»
现在,我们可以从一开始就指定我们的原始值,并且可以随时用其他值替换它们。 我们甚至可以变得时髦,并在每个容器上放置不同的类型约束:
my $list := (my Int $ = 1, my Str $ = '2', my Rat $ = 3.0);
$list[0] = 100; # OK!
$list[1] = 42; # Typecheck failure!
# OUTPUT: «Type check failed in assignment;
# expected Str but got Int (42) […] »
my Positional $list := (my $ = 1, my $ = '2', my $ = 3.0);
不知你咋想的,但是这对我来说看起来非常冗长。 幸运的是,Perl 6 有语法来简化它!
Position@lly
my @list := 42;
# OUTPUT: «Type check failed in binding;
# expected Positional but got Int (42) […] »
my @list := [1, '2', 3.0];
@list[0] = 100;
@list[0] = 200;
say @list;
# OUTPUT: «[200 2 3]
»
my @list = 1, '2', 3.0;
好,简洁。
my %hash = :42foo, :bar<ber>;
say %hash; # OUTPUT: «{bar => ber, foo => 42}
»
my &reversay = sub { $^text.flip.say }
reversay '6 lreP ♥ I'; # OUTPUT: «I ♥ Perl 6
»
# store a different Callable in the same variable
&reversay = *.uc.say; # a WhateverCode object
reversay 'I ♥ Perl 6'; # OUTPUT: «I ♥ PERL 6
»
The One and Only
my $listish = (1, 2, 3);
say $listish; # OUTPUT: «(1 2 3)
»
# Binding:
my $list := (1, 2, 3);
say $list.perl;
say "Item: $_" for $list;
# OUTPUT:
# (1, 2, 3)
# Item: 1
# Item: 2
# Item: 3
# Assignment:
my $listish = (1, 2, 3);
say $listish.perl;
say "Item: $_" for $listish;
# OUTPUT:
# $(1, 2, 3)
# Item: 1 2 3
my @stuff = (1, 2, 3), %(:42foo, :70bar);
say "List Item: $_" for @stuff[0];
say "Hash Item: $_" for @stuff[1];
# OUTPUT:
# List Item: 1 2 3
# Hash Item: bar 70
# foo 42
my @stuff = (1, 2, 3), %(:42foo, :70bar);
say flat @stuff;
# OUTPUT: «((1 2 3) {bar => 70, foo => 42})
»
-> *@args { @args.say }(@stuff)
# OUTPUT: «[(1 2 3) {bar => 70, foo => 42}]
»
正是这种行为可以将 Perl 6 初学者推上墙,特别是那些来自 Perl 5 自动展平语言的人。然而,现在我们知道为什么会出现这种行为,我们可以改变它!
Decont
my @stuff = (1, 2, 3), %(:42foo, :70bar);
say "Item: $_" for @stuff[0]<>;
say "Item: $_" for @stuff[1]<>;
# OUTPUT:
# Item: 1
# Item: 2
# Item: 3
# Item: bar 70
# Item: foo 42
my @stuff = (1, 2, 3), %(:42foo, :70bar);
say flat @stuff»<>;
# OUTPUT: «(1 2 3 bar => 70 foo => 42)
»
-> *@args { @args.say }(@stuff»<>)
# OUTPUT: «[1 2 3 bar => 70 foo => 42]
»
my @stuff := (1, 2, 3), %(:42foo, :70bar);
say flat @stuff;
# OUTPUT: «(1 2 3 bar => 70 foo => 42)
»
-> *@args { @args.say }(@stuff)
# OUTPUT: «[1 2 3 bar => 70 foo => 42]
»
不要让它溜走
my @stuff = (1, 2, 3), (4, 5);
say "Item: $_" for |@stuff[0];
# OUTPUT:
# Item: 1
# Item: 2
# Item: 3
虽然它可以完成工作,但可能会引入微妙的 bugs,这些 bug 可能很难追查到。 尝试在这里找到一个,在一个程序中迭代了一个无限的非负整数列表,并打印那些素数:
my $primes = ^∞ .grep: *.is-prime;
say "$_ is a prime number" for |$primes;
# CAREFUL! Don't consume all of your resources!
my $primes = ^∞ .map: *.self;
Nil for |$primes;
让我们再试一次,但是这次使用 decont 方法 op:
my $primes = ^∞ .map: *.self;
Nil for $primes<>;
my $primes := ^∞ .map: *.self;
Nil for $primes;
I Want Less
如果你讨厌符号,Perl 6 会得到一些你可以微笑的东西:无符号的变量。 只要在声明中加一个反斜杠的前缀,表示你不想要讨厌的符号:
my \Δ = 42;
say Δ²; # OUTPUT: «1764
»
my \Δ = my $ = 42;
Δ = 11;
say Δ²; # OUTPUT: «121
»
sub sigiled ($x is raw, +@y) {
$x = 100;
say flat @y
}
sub sigil-less (\x, +y) {
x = 200;
say flat y
}
my $x = 42;
sigiled $x, (1, 2), (3, 4); # OUTPUT: «((1 2) (3 4))
»
say $x; # OUTPUT: «100
»
sigil-less $x, (1, 2), (3, 4); # OUTPUT: «(1 2 3 4)
»
say $x; # OUTPUT: «200
»
Defaulting on Default Defaults
容器提供的一个很棒的功能是默认值。 你可能听说过在 Perl 6 中,Nil
表示缺少一个值,而不是一个值。 容器默认值就是它的作用:
my $x is default(42);
say $x; # OUTPUT: «42
»
$x = 10;
say $x; # OUTPUT: «10
»
$x = Nil;
say $x; # OUTPUT: «42
»
my @a is default<meow> = 1, 2, 3;
say @a[0, 2, 42]; # OUTPUT: «(1 3 meow)
»
@a[0]:delete;
say @a[0]; # OUTPUT: «meow
»
my %h is default(Nil) = :bar<ber>;
say %h<bar foos>; # OUTPUT: «(ber Nil)
»
%h<bar>:delete;
say %h<bar> # OUTPUT: «Nil
»
容器的默认值有一个默认的默认值:容器上的显式类型约束:
say my Int $y; # OUTPUT: «(Int)
»
say my Mu $z; # OUTPUT: «(Mu)
»
say my Int $i where *.is-prime; # OUTPUT: «(<anon>)
»
$i.new; # OUTPUT: (exception) «You cannot create […]»
say my $x; # OUTPUT: «(Any)
»
say $x = Nil; # OUTPUT: «(Any)
»
请注意,您可能在可选参数的例程签名中使用的默认值不是容器默认值,将 Nil
分配给子例程参数或分配给参数不会使用签名中的默认值。
自定义
my $collector := do {
my @stuff;
Proxy.new: :STORE{ @stuff.push: @_[1] },
:FETCH{ @stuff.join: "|" }
}
$collector = 42;
$collector = 'meows';
say $collector; # OUTPUT: «42|meows
»
$collector = 'foos';
say $collector; # OUTPUT: «42|meows|foos
»
class Foo {
has $!foo;
method foo {
Proxy.new: :STORE(-> $, Int() $!foo { $!foo }),
:FETCH{ $!foo }
}
}
my $o = Foo.new;
$o.foo = ' 42.1e0 ';
say $o.foo; # OUTPUT: «42
»
这就是全部,伙计
那关于这一切。 在 Perl 6 中你将会看到的剩下的动物是 “twigils”:名称前带有两个符号的变量,但是就容器而言,它们的行为与我们所介绍的变量相同。 第二个符号只是表示附加信息,如变量是隐含的位置参数还是命名参数...
sub test { say "$^implied @:parameters[]" }
test 'meow', :parameters<says the cat>;
# OUTPUT: «meow says the cat
»
...或者该变量是私有属性还是公共属性:
with class Foo {
has $!foo = 42;
has @.bar = 100;
method what's-foo { $!foo }
}.new {
say .bar; # OUTPUT: «[100]
»
say .what's-foo # OUTPUT: «42
»
}
然而,这是另一天的旅程。
结论
Perl 6 有一个丰富的变量和容器系统,与 Perl 5 有很大的不同。理解它的工作方式是非常重要的,因为它会影响列表和哈希行为的迭代和展开方式。
在 Perl 6 中存在无符号变量,它们与具有绑定功能的 $
-sigiled 变量具有相似的行为。 当用作参数时,这些变量的行为就像应用了 is raw
trait一样。
最后,容器可以有默认值,可以创建自己的自定义容器,可以绑定到变量或从例程返回。
节日快乐!