搜索
您的当前位置:首页正文

第二天 - Perl 6: 符号, 变量和容器

来源:二三娱乐

第二天-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一样。

最后,容器可以有默认值,可以创建自己的自定义容器,可以绑定到变量或从例程返回。

节日快乐!

Top