13

我有时会看到(对我而言)在变量前面使用错误符号的代码

my $arr  = [1, 2, 3, 4, 5];      # an array
my $lst  = (1, 2, 3, 4, 5);      # a list
my $hash = {a => '1', b => '2'}; # a hash
my $func = -> $foo { say $foo }; # a callable

这一切都完全按预期工作

say $arr[0];    # 1
say $lst[1];    # 2
say $hash<a>;   # 1
say $hash{'b'}; # 2
$func('hello'); # hello

Q1:使用标量容器而不是仅仅使用“正确”容器有什么好处?

我知道 Perl 只让集合存储标量,需要通过数组引用来完成多维数组之类的事情,[...]分别{...}是数组和哈希引用文字。

为了扩展和澄清我在这里的意思,基本上有两种定义事物的方法,按值和按引用:

# "values"
my @arr = (1, 2, 3, 4);
my %hash = (1 => 2, 3 => 4); 

# which are accessed like this:
my $result1 = $arr[0];
my $result2 = $hash{1};

# references (note how the braces canged)
my $aref = [1, 2, 3, 4];
my $href = {1 => 2, 3 => 4};

# or making a reference to existing collections
my $aref2 = \@arr;
my $href2 = \%hash;

# which are accessed like this:
my $result3 = $aref->[0];
my $result4 = $href->{1};

这种疯狂背后的原因是 Perl 集合只接受标量,而引用就是这样。使用引用本质上是一种启用多维数组的方法。

TL;DR,这种区别在 Perl 中是有意义的,因为它们服务于两个截然不同的目的。

Q2:我们是在再次处理类似 Perl 5 的引用文字,还是有别的东西在起作用?

4

2 回答 2

10

TL;DR对于计算机、人类以及 Raku,非标量(复数)也是标量(单数)。(反之可能不成立。)例如,anArray既是复数事物(元素数组)又是单一事物 an Array。当您希望在句法上和静态上强调数据最通用的单数性质时,请使用$.

这是一个基于@sid_com++ 评论的开场示例:

my @a = ( 1, 2 ), 42, { :a, :b }
for @a -> $b {say $b}            # (1 2)␤42␤{a => True, b => True}␤ 
for @a -> @b {say @b}            # (1 2)␤Type check failed ...

第一个循环将值绑定到$b. 它是“容错的”,因为它接受任何值。第二个循环绑定到@b. 任何不Positional发挥作用的值都会导致类型检查失败。

我的 Raku 相当于你的 Perl 代码

这是您的 Perl 代码的 Raku 翻译:

my @arr = (1, 2, 3, 4);
my %hash = (1 => 2, 3 => 4); 

my $result1 = @arr[0];                          # <-- Invariant sigil
my $result2 = %hash{1};                         # <-- Invariant sigil

my $aref = [1, 2, 3, 4];
my $href = {1 => 2, 3 => 4};

my $aref2 = @arr;                               # <-- Drop `\`
my $href2 = %hash;                              # <-- Drop `\`

my $result3 = $aref[0];                         # <-- Drop `->`
my $result4 = $href{1};                         # <-- Drop `->`

代码有点短。惯用代码可能会更短一些,删除:

  • ref变量。变量@foo 参考。术语([...]名词)位置参考Array文字。很少或不需要使用标量变量来显式存储引用。

  • 前几行中的括号;

  • 大多数右大括号后面的分号是一行中的最后一个代码;

Raku 的印记是不变的。下面是两张表格,提供了 Perl 的 sigil 变化与 Raku 的 sigil invariance 的一目了然的比较

为什么要打扰印记?

所有印记变体直接对应于将“类型”信息嵌入到人类、语言和编译器可见的标识符名称中:

  • foo告诉 Raku 功能应该根据数据的运行时类型来决定在单数和复数对数据的操作方式之间进行选择。

  • $foo告诉 Raku 选择奇异的行为。例如,一个值可能是List包含许多值的 a,但它的奇异性质反而被强调了。

  • &foo类型检查绑定或分配的值是否Callable发挥作用。

  • @foo告诉 Raku 选择Iterable行为。还要键入检查绑定值是否Positional起作用。可以绑定a Listor ,但尝试绑定or a会产生类型错误。Array42Hash

  • %foo告诉 Raku 选择Iterable行为。还要键入检查绑定值是否Associative起作用。可以绑定a Pairor ,但尝试绑定or a会产生类型错误。Bag42List

接下来,我将考虑您对每个印记替代方案的问题。

削减印记

重复你的例子,但这次“削减”印记:

my \arr  = [1, 2, 3, 4, 5];      # an array
my \lst  = (1, 2, 3, 4, 5);      # a list
my \hash = {a => '1', b => '2'}; # a hash
my \func = -> \foo { say foo };  # a callable

这些几乎完全按预期工作:

say arr[0];     # 1
say lst[1];     # 2
say hash<a>;    # 1
say hash{'b'};  # 2
func.('hello'); # hello

请参阅$ vs &下面的内容,了解为什么func.(...)不只是func(...). 最后一个 nosigil 案例没有什么影响,因为在 Raku 中,通常会这样写道:

sub func (\foo) { say foo }
func('hello'); # hello

标志被削减的标识符是SSA 形式。也就是说,它们在编译时永久绑定到它们的数据。值类型数据是不可变的。引用也是不可变的(尽管它引用的数据可以更改),因此例如,如果它是一个数组,它将保持相同的数组。

(请参阅禁止重新绑定无符号变量是否有目的或好处?进一步讨论。)

$foo而不是@foo

乐支持:

  • 惰性列表。(这可能非常有用。)

  • 一个布尔.is-lazy方法,指示列表分配 ( @foo = ...) 是否应将分配的对象视为惰性或急切。重要的是,允许返回惰性列表False。(这也很有用。)

  • 无限的惰性列表。(还有另一件事可能非常有用。)

以上三个功能单独使用,也可以一起使用。但是,尽管 Raku 不尝试以其他方式监管这些功能是合适的,但需要遵守规则以避免出现问题。最简单的方法是在重要时使用正确的印记,如下所述。

假设infinite是一个无限的惰性列表,它False返回.is-lazy

my $foo = infinite;
say $foo[10];        # displays 11th element
my @foo = infinite;

前两行工作正常。第三个挂起,试图将无限数量的元素复制到@foo.


是一件事还是多件事?当然,如果是列表,则两者都是:

my $list = <a b c> ;
my @list = <a b c> ;
my \list = <a b c> ;
.say for $list ;      # (a b c)␤   <-- Treat as one thing
.say for @list ;      # a␤b␤c␤    <-- Treat as plural thing
.say for  list ;      # a␤b␤c␤    <-- Go by bound value, not sigil

上面选择的 sigil 只是表明您希望语言结构和读者默认采用的视图。如果您愿意,您可以自行反转:

.say for @$list ;     # a␤b␤c␤
.say for $@list ;     # [a b c]␤
.say for $(list)      # (a b c)␤

分配不同:

my ($numbers, $letters) = (1, 2, 3), ('a', 'b', 'c');
say $numbers;                                            # (1 2 3)
say $letters;                                            # (a b c)
my (@numbers, @letters) = (1, 2, 3), ('a', 'b', 'c');
say @numbers;                                            # [(1 2 3) (a b c)]
say @letters;                                            # []

@变量的赋值会“啜饮”所有剩余的参数。(与调用标量语义:=之类的元操作绑定Z=,即不要啜饮。)

我们在这里看到另一个区别;分配给$变量将保留 a ListList但分配给@变量会将其值“啜饮”到变量绑定的任何容器@中(默认情况下为 an Array)。


一个小事情是字符串插值:

my $list := 1, 2;
my @list := 1, 2;
say "\$list = $list; \@list = @list"; # $list = 1 2; @list = @list
say "@list @list[] @list[1]";         # @list 1 2 2

$foo而不是%foo

再说一遍,是一件事还是多件事?如果是哈希,则两者兼而有之。

my $hash = { :a, :b }
my %hash =   :a, :b ;
my \hash = { :a, :b }
.say for $hash ;      # {a => True, b => True}␤   <-- By sorted keys
.say for %hash ;      # {b => True}␤{a => True}␤  <-- Random order
.say for  hash ;      # {a => True}␤{b => True}␤  <-- Random order

赋值和字符串插值也以类似于@.

$foo而不是&foo

本节只是为了完整性。它只显示了一个使用$. 我刚刚弥补了这个答案——我不记得看到有人使用它。

与其他 sigil 替代品一样,主要区别在于您是否想要强调Callable可调用对象的性质。

作为设置,请注意subRaku 中的声明声明了一个带有符号的相应常量标识符&

sub foo (--> Int) { 42 }
say foo;                     # 42
say &foo.signature;          # ( --> Int)
&foo = 99;                   # Cannot modify an immutable Sub...

这意味着如果您使用 sigil 声明一个可变的例程变量,您可以在没有&sigil 的情况下调用它:

my &bar = { 99 }
say bar;                     # 99
&bar = { 100 }
say bar;                     # 100

如果您想声明一个可变的例程变量并且不允许在没有 sigil 的情况下轻松调用它,您可以$改为声明它:

my Callable $baz = { 101 }
say baz;                     # Undeclared routine: baz
say $baz();                  # 101   <-- Need both sigil and parens

顺便说一句,这就是你得到的原因:

my \func = -> \foo { say foo }
func('hello');  # Variable '&func' is not declared

参考文字

Q2:我们是在再次处理类似 Perl 5 的引用文字,还是有别的东西在起作用?

尽管有你的例子,知道 Perl(至少我在上个世纪做过),并且思考过你写的东西,但我仍然不清楚你在问什么。

广泛的编程语言采用[...]术语(名词)位置作为对文字数组的引用。其他数据结构文字还有其他常见约定。这就是Raku所做的。

因此可以写:

my $structure =
[ 0, [ 99, [ ( 1, 2, 3), { key => [ 4, 5, | < a b >, c => 42 ] } ], ], ] ;

say $structure[1][1][1]<key>[4]<c> ; # 42

你说的是那种东西吗?

取消引用文字

postcircumfix:< [ ] >被声明为一堆(应该)Positional在其左参数上应用一致的索引协议的多子。

  • Positional所有完成角色工作的内置类型。

  • Positional执行该角色的用户定义类型应该可以工作,因为该角色定义了必须由执行该角色的类型实现的类型化接口存根。

  • 但是ducktyping也可以;如果一个类型实现了postcircumfix:< [ ] >它应该工作的接口的基础知识。

同样的故事适用于postcircumfix:< { } >and postcircumfix:« < > »,但相关的角色/协议是Associative一致的索引。

类似的故事也适用于postcircumfix:< ( ) >Callable

于 2020-06-01T23:20:37.163 回答
4

已经有一些很好的答案!如果想进一步了解这个一般性主题,我可以推荐第 2 天 - Perl 6:Sigils、Variables 和 Containers吗?它帮助我理解了一些相关主题,例如作为容器的标量和decont op <>$我认为这些示例可能会为和@/%管理按预期有效打包/解包数据结构的微妙之处的相互作用提供更多理由。

于 2020-06-03T20:53:13.060 回答