Perl 6 中变量命名的工作原理

在本系列比较 Perl 5 和 Perl 6 的第五篇文章中,了解 Perl 6 中 sigil 的使用。
217 位读者喜欢这篇文章。
Woman programming

WOCinTech Chat。由 Opensource.com 修改。CC BY-SA 4.0

在本系列比较 Perl 5 和 Perl 6 的前四篇文章中,我们研究了您在 迁移代码时 可能遇到的一些问题,垃圾回收 的工作原理,为什么 容器取代了引用,以及 Perl 6 中 (子例程) 签名 的使用以及这些内容与 Perl 5 的不同之处。

在第五篇文章中,我们将研究 Perl 5 和 Perl 6 之间 sigil(变量名开头的符号)的细微差别。

概述

让我们从 Perl 5 和 Perl 6 中 sigil 的概述开始

Sigil Perl 5 Perl 6
@ 数组 位置型
% 哈希 关联型
& 子例程 可调用型
$ 标量
* 类型团 不适用

@(数组 vs. 位置型)

当您在 Perl 5 中定义数组时,您将创建一个可扩展的标量值列表,并使用 sigil @ 为其命名

# Perl 5
my @foo = (1,2,3);
push @foo, 42;
say for @foo;  # 1␤2␤3␤42␤

当您在 Perl 6 中定义数组时,您将创建一个新的 Array 对象,并将其绑定到词法作用域中该名称的条目。所以

# Perl 6
my @foo = 1,2,3;
push @foo, 42;
.say for @foo;  # 1␤2␤3␤42␤

在功能上与 Perl 5 相同。但是,第一行是以下内容的语法糖

# Perl 6
my @foo := Array.new( 1,2,3 );

这会将一个新的 Array 对象绑定(而不是赋值)到词法定义的名称 @foo。Perl 6 中的 @ sigil 指示类型约束:如果您想将某些内容绑定到带有该 sigil 的词法作用域条目中,它必须执行 Positional 角色。使用智能匹配很容易确定类是否执行了某个角色

# Perl 6
say Array ~~ Positional;   # True

您可以认为 Perl 6 中的所有数组都以与 Perl 5 中 tied arrays 的实现方式相同的方式实现。这离事实不远。在不深入细节的情况下,一个简单的例子可能会说明这一点。AT-POS 方法是实现 Positional 角色的类的关键方法之一。每当需要访问单个元素时,都会调用此方法。所以,当您编写

say @a[42];

您正在执行

say @a.AT-POS(42);

当然,这并不是您可以实现的唯一方法;还有 更多方法

与其必须绑定您的类来执行 Positional 角色,不如使用 is 特征的特殊语法。因此,不必编写

# Perl 6
my @a := YourClass.new( 1,2,3 );

您可以编写

# Perl 6
my @a is YourClass = 1,2,3;

在 Perl 5 中,与“普通”数组相比,tied arrays 的速度非常慢。在 Perl 6 中,数组在启动时也同样慢。幸运的是,Rakudo Perl 6 通过内联和“即时编译 (JITing)” 操作码 到机器代码(如果可能)来优化热代码路径。(由于优化器的进步,这种情况发生得更早、更频繁、更好)。

%(哈希 vs. 关联型)

Perl 6 中的哈希的实现方式与数组类似;您也可以将它们视为 tied hash(使用 Perl 5 术语)。与用于实现数组的 Positional 角色不同,应该使用 Associative 角色来实现哈希。

同样,一个简单的例子可能会有所帮助。AT-KEY 方法是实现 Associative 角色的类的关键方法之一。每当需要访问特定键的值时,都会调用此方法。所以,当您编写

say %h<foo>

您正在执行

say %h.AT-KEY("foo");

当然,还有 许多其他方法 您可以实现。

&(子例程 vs. 可调用型)

在 Perl 5 中,只有一种可调用的可执行代码类型,即子例程

# Perl 5
sub frobnicate { shift ** 2 }

并且,如果您想将子例程作为参数传递,则需要获取对其的引用

# Perl 5
sub do_stuff_with {
    my $lambda = shift;
    &$lambda(shift);
}
say do_stuff_with( \&frobnicate, 42 );  # 1764

在 Perl 6 中,多种类型的对象可以包含可执行代码。它们的共同之处在于它们都使用了 Callable 角色

& sigil 强制绑定到执行 Callable 角色的对象,就像 % sigil 对 Associative 角色和 @ sigil 对 Positional 角色所做的那样。一个非常接近 Perl 5 的例子是

# Perl 6
my &foo = sub ($a,$b) { $a + $b }
say foo(42,666);  # 708

请注意,即使变量具有 & sigil,您也需要使用它来执行该变量中的代码。实际上,如果您在 BEGIN 块中运行代码,则与普通的 sub 声明相比,将没有任何区别

# Perl 6
BEGIN my &foo = sub ($a,$b) { $a + $b } # same as sub foo()

与 Perl 5 相比,在 Perl 6 中,BEGIN 块可以是单个语句而无需块,因此它与外部共享其词法作用域。但我们将在以后的文章中更多地讨论这一点。

使用 & sigil 变量的主要优点是,在编译时就知道那里将有一些可执行的东西,即使该东西尚不为人所知。

还有其他方法可以设置一段代码以供执行

# Perl 6
my &boo = -> $a, $b { $a + $b }  # same, using a Block with a signature
my &goo = { $^a + $^b }          # same, using auto-generated signature
my &woo = * + *;                 # same, using Whatever currying

如果您想了解更多

您使用的那一种取决于具体情况和您的偏好。

最后,您还可以在签名内使用 & sigil 来指示被调用者想要在那里放置一些可执行的东西。这使我们回到了本节中的前两个代码示例

# Perl 5
sub frobnicate { shift ** 2 }
sub do_stuff_with {
    my $lambda = shift;
    &$lambda(shift);
}
say do_stuff_with( \&frobnicate, 42 );  # 1764
# Perl 6
sub frobnicate { $^a ** 2 }
sub do-stuff-with(&lambda, $param) { lambda($param) }
say do-stuff-with( &frobnicate, 42 );  # 1764

请注意,在 Perl 6 中,您不需要获取引用;您可以简单地将代码对象(由 & sigil 指示)作为参数传递。

$(标量 vs. 项)

@%& sigil 相比,$ sigil 有点平淡。它不强制任何类型检查,因此您可以将其绑定到任何类型的对象。因此,当您编写

# Perl 6
my $answer = 42;

会发生类似这样的事情

# Perl 6
my $answer := Scalar.new(42);

只是在非常低的级别。因此,如果您想知道,这段代码将不起作用。这就是声明标量变量的全部内容。

在 Perl 6 中,$ 还表示无论其中包含什么都应被视为单个项。因此,即使标量容器中填充了 Array 对象,在需要迭代的情况下,它也会被视为单个项

# Perl 6
my @foo = 1,2,3;
my $bar = Array.new(1,2,3);  # alternately: [1,2,3]
.say for @foo;  # 1␤2␤3␤
.say for $bar;  # [1 2 3]

请注意,后一种情况只进行一次迭代,而前一种情况进行三次迭代。您可以通过在适当的 sigil 前面加上前缀来指示是否希望某些内容迭代

# Perl 6
.say for $@foo;  # [1 2 3] , consider the array as an item
.say for @$bar;  # 1␤2␤3␤  , consider the scalar as a list

但也许这使我们过于深入了行噪声的领域。幸运的是,还有更详细的等效项

# Perl 6
.say for @foo.item;  # [1 2 3] , consider the array as an item
.say for $bar.list;  # 1␤2␤3␤  , consider the scalar as a list

*(类型团)

您可能已经注意到,Perl 6 没有 * sigil,也没有 类型团 的概念。如果您不知道类型团是什么,则不必担心。您完全可以不知道 Perl 5 中符号表的复杂性(您可以跳过下一段)。

在 Perl 6 中,sigil 是存储在 符号表 中的名称的一部分,而在 Perl 5 中,名称存储时不带 sigil。例如,在 Perl 5 中,如果您在程序中引用 $foo,编译器将查找 foo(不带 sigil),然后获取关联的信息(这是一个数组),并查找 $ sigil 的索引处需要的内容。在 Perl 6 中,如果您引用 $foo,编译器将查找 $foo 并直接使用与该键关联的信息。

不要将 Perl 6 中用于指示参数 slurpiness 的 * 与 Perl 5 中的类型团 sigil 混淆——它们彼此无关。

无 sigil 变量

Perl 5 不直接支持无 sigil 变量(除了可能是左值子例程,但这确实会非常笨拙)。

Perl 6 也不直接支持无 sigil 变量,但它支持通过在定义中为名称添加反斜杠 (\) 前缀来绑定到无 sigil 名称

# Perl 6
my \the-answer = 42;
say the-answer;  # 42

由于赋值的右侧是常量,因此这基本上与定义常量相同

# Perl 5
use constant the_answer => 42;
say the_answer;  # 42
# Perl 6
my constant the-answer = 42;
say the-answer;  # 42

如果定义的右侧是其他内容,则会更有趣。类似于容器!这允许使用以下语法技巧来获取无 sigil 变量

# Perl 6
my \foo = $ = 41;                # a sigilless scalar variable
my \bar = @ = 1,2,3,4,5;         # a sigilless array
my \baz = % = a => 42, b => 666; # a sigilless hash

这基本上创建了无名词法实体(标量、数组和哈希),使用正常的语义初始化它们,然后将生成的对象(Scalar 容器、Array 对象和 Hash 对象)绑定到无 sigil 名称,您可以像在 Perl 6 中使用任何其他普通变量一样使用它。

# Perl 6
say ++foo;     # 42
say bar[2];    # 3
bar[2] = 42;
say bar[2];    # 42
say baz<a b>;  # (42 666)

当然,通过这样做,您将失去 sigil 的所有优势,特别是关于插值。然后,您将始终需要在插值中使用 { }

# Perl 6
say "The answer is {the-answer}.";  # The answer is 42.

在大多数 Perl 5 版本中,等效项更加繁琐

# Perl 5
say "The answer is @{[the_answer]}.";  # The answer is 42.

总结

当使用 Perl 5 概念考虑 Perl 6 中的所有变量时,都可以将其视为 tied variables。这使得它们最初有些慢。但是,运行时优化和热代码路径的 JITing(在某个时候到机器代码)已经使它在某些基准测试中比 Perl 5 变量更快。

Perl 6 中的 @%& 不会创建任何特定对象,而是指示将应用于名称绑定到的对象的类型约束。$ sigil 在这方面有所不同,因为它没有要强制执行的类型约束。

@$ 前缀分别指示列表化和项化,尽管使用 .list.item 方法可能更具可读性。

通过一些语法技巧,您可以在程序 Perl 6 时不使用变量名中的任何 sigil。

下一步阅读
Elizabeth Mattijsen
Elizabeth Mattijsen 自 1978 年以来一直以编程为生,使用过各种(现在大多已过时)编程语言,之后她开始使用 Perl 4 编程。1994 年,她创立了荷兰第一家商业网站开发公司,使用 Perl 5 作为主要编程语言。从 2003 年起,她参与了一项在线酒店预订服务的快速增长。

评论已关闭。

© . All rights reserved.