在本系列比较 Perl 5 和 Perl 6 的第一篇文章中,我们研究了将代码迁移到 Perl 6 时可能遇到的一些问题。在第二篇文章中,我们研究了 Perl 6 中的垃圾回收工作原理。在这里,在第三篇文章中,我们将重点关注 Perl 5 的引用以及它们在 Perl 6 中的处理方式,并介绍绑定和容器的概念。
引用
Perl 6 中没有引用,这对于许多习惯了 Perl 5 语义的人来说是令人惊讶的。但请不要担心:因为没有引用,您不必担心是否应该取消引用。
# Perl 5
my $foo = \@bar;   # must add reference \ to make $foo a reference to @bar
say @bar[1];       # no dereference needed
say $foo->[1];     # must add dereference ->
# Perl 6
my $foo = @bar;    # $foo now contains @bar
say @bar[1];       # no dereference needed, note: sigil does not change
say $foo[1];       # no dereference needed either有人可能会认为 Perl 6 中的一切都是引用。从 Perl 5(对象是祝福的引用)的角度来看,这对于 Perl 6 来说是一个合乎逻辑的结论,因为一切都是对象(或可以被视为对象)。但这并不能公正地反映 Perl 6 的情况,并且会阻碍您理解 Perl 6 中的工作方式。谨防虚假朋友!
绑定
在我们讨论赋值之前,重要的是要理解 Perl 6 中的绑定概念。您可以使用 := 运算符将某些内容显式绑定到其他内容。当您定义词法变量时,可以将值绑定到它
my $foo := 42;  # note: := instead of =简而言之,这会在词法 pad (lexpad)(您可以将其视为编译时哈希,其中包含有关在该词法作用域中可见的事物的信息)中创建一个名为“$foo”的键,并将 42 作为其字面值。因为这是一个字面常量,所以您无法更改它。尝试这样做会导致异常。所以不要那样做!
此绑定操作在许多情况下在后台使用,例如在迭代时
my @a = 0..9;    # can also be written as ^10
say @a;          # [0 1 2 3 4 5 6 7 8 9]
for @a { $_++ }  # $_ is bound to each array element and incremented
say @a;          # [1 2 3 4 5 6 7 8 9 10]如果您尝试迭代常量列表,则 $_ 将绑定到字面值,您不能递增这些值
for 0..9 { $_++ }  # error: requires mutable arguments赋值
如果您比较 Perl 5 和 Perl 6 中“创建词法变量并赋值给它”,则从表面上看它们是相同的
my $bar = 56;  # both Perl 5 and Perl 6在 Perl 6 中,这也会在 lexpad 中创建一个名为“$bar”的键。但是,不是直接将值绑定到该 lexpad 条目,而是为您创建一个容器(一个 Scalar 对象),并将该容器绑定到“$bar”的 lexpad 条目。然后,56 作为值存储在该容器中。在伪代码中,您可以将其视为
my $bar := Scalar.new( value => 56 );请注意,Scalar 对象是绑定的,而不是赋值的。在 Perl 5 中,最接近这种情况的是tied scalar。但当然,“= 56”要少打字得多!
诸如 Array 和 Hash 之类的数据结构也会自动将值放入绑定到结构的容器中。
my @a;       # empty Array
@a[5] = 42;  # bind a Scalar container to 6th element and put 42 in it容器
Scalar 容器对象对于 Perl 6 中的大多数操作是不可见的,因此大多数时候您不必考虑它。例如,每当您使用变量作为参数调用子例程(或方法)时,它都会绑定到容器中的值。并且因为您无法赋值给值,所以您会得到
sub frobnicate($this) {
    $this = 42;
}
my $foo = 666;
frobnicate($foo); # Cannot assign to a readonly variable or a value如果您想允许赋值给外部值,您可以将 is rw 特征添加到签名中的变量。这将签名中的变量绑定到指定变量的容器,从而允许赋值
sub oknicate($this is rw) {
    $this = 42;
}
my $foo = 666;
oknicate($foo); # no problem
say $foo;       # 42代理
从概念上讲,Perl 6 中的 Scalar 对象具有 FETCH 方法(用于生成对象中的值)和 STORE 方法(用于更改对象中的值),就像 Perl 5 中的 tied scalar 一样。
假设您稍后将值 768 赋值给 $bar 变量
$bar = 768;发生的事情在概念上等同于
$bar.STORE(768);假设您想将 20 添加到 $bar 中的值
$bar = $bar + 20;概念上发生的事情是
$bar.STORE( $bar.FETCH + 20 );如果您想在容器上指定自己的 FETCH 和 STORE 方法,您可以通过绑定到 Proxy 对象来做到这一点。例如,要创建一个始终报告赋值给它的值两倍的变量
my $double := do {  # $double now a Proxy, rather than a Scalar container
    my $value;
    Proxy.new(
      FETCH => method ()     { $value + $value },
      STORE => method ($new) { $value = $new }
    )
}请注意,您将需要一个额外的变量来保存存储在此类容器中的值。
约束和默认值
除了值之外,Scalar 还包含额外的信息,例如类型约束和默认值。采用以下定义
my Int $baz is default(42) = 666;它创建一个名为“$baz”的 Scalar 绑定到 lexpad,将该容器中的值约束为成功智能匹配 Int 的类型,将容器的默认值设置为 42,并将值 666 放入容器中。
将字符串赋值给该变量将因类型约束而失败
$baz = "foo";
# Type check failed in assignment to $baz; expected Int but got Str ("foo")如果您在定义变量时未给出类型约束,则将假定 Any 类型。如果您未指定默认值,则将假定类型约束。
将 Nil(Perl 6 中等同于 Perl 5 的 undef)赋值给该变量将将其重置为默认值
say $baz;   # 666
$baz = Nil;
say $baz;   # 42总结
Perl 5 具有值和对值的引用。Perl 6 没有引用,但它有值和容器。Perl 6 中有两种类型的容器:Proxy(很像 Perl 5 中的 tied scalar)和 Scalar。简而言之,变量以及 List、Array 或 Hash 的元素,要么是值(如果是绑定的),要么是容器(如果是赋值的)。每当调用子例程(或方法)时,给定的参数都会被解除容器化并绑定到子例程的参数(除非另有说明)。容器还保留诸如类型约束和默认值之类的信息。将 Nil 赋值给变量会将其返回到其默认值,如果您未指定类型约束,则默认值为 Any。
 
 
 
 
 

1 条评论