TL;DR 对于计算机、人类以及 Raku,非标量(复数)也是标量(单数)。 (反之可能不正确。)例如,Array 既是复数事物(元素数组)又是单个事物,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。它是“容错的”,因为它接受 any 值。第二个循环绑定到@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 `->`
代码有点短。惯用代码可能会更短一些,删除:
Raku 的印记是不变的。这里是two tables providing an at-a-glance comparison of Perl's sigil variation vs Raku's sigil invariance。
为什么要使用印记?
所有印记变体直接对应于将“类型”信息嵌入到人类、语言和编译器可见的标识符名称中:
-
foo 告诉 Raku 功能应该根据数据的运行时类型来决定在单数和复数对数据的操作方式之间进行选择。
-
$foo 告诉 Raku 选择单一行为。例如,一个值可能是一个包含许多值的List,但它的奇异性质却被强调了。
-
&foo 类型检查绑定或分配的值是否具有Callable 角色。
-
@foo 告诉 Raku 选择 Iterable 行为。还要键入检查绑定值是否具有Positional 角色。可以绑定 List 或 Array,但尝试绑定到 42 或 Hash 会产生类型错误。
-
%foo 告诉 Raku 选择 Iterable 行为。还要键入检查绑定值是否具有Associative 角色。可以绑定 Pair 或 Bag,但尝试绑定到 42 或 List 会产生类型错误。
接下来我会考虑你对每个印记备选方案的问题。
削减印记
重复你的例子,但这次“削减”印记:
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 form。也就是说,它们在编译时永久绑定到它们的数据。值类型数据是不可变的。引用也是不可变的(尽管它引用的数据可以更改),因此例如,如果它是一个数组,它将保持相同的数组。
(更多讨论请参见Is there a purpose or benefit in prohibiting sigilless variables from rebinding?。)
$foo 而不是 @foo?
乐乐支持:
以上三个功能单独使用,也可以一起使用。但是,尽管 Raku 不尝试以其他方式监管这些功能是合适的,但需要遵守规则以避免出现问题。最简单的方法是在重要的时候使用正确的印记,如下所述。
假设infinite 是一个无限惰性列表,它为.is-lazy 返回False:
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 ; # abc <-- Treat as plural thing
.say for list ; # abc <-- Go by bound value, not sigil
上面选择的 sigil 只是表示您希望语言构造和读者默认采用的视图。如果您愿意,您可以自行反转:
.say for @$list ; # abc
.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= 之类的元操作绑定调用标量语义,即不要啜饮。)
我们在这里看到了另一个不同之处;分配给$ 变量将保留List 和List,但是分配给@ 变量“slurps”它的值进入 绑定@ 变量的任何容器到(默认情况下,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 特性。
作为设置,请注意 Raku 中的 sub 声明声明了一个带有 & sigil 的相应常量标识符:
sub foo (--> Int) { 42 }
say foo; # 42
say &foo.signature; # ( --> Int)
&foo = 99; # Cannot modify an immutable Sub...
这意味着如果您使用& 印记声明一个可变的例程变量,您可以在没有印记的情况下将其称为:
my &bar = { 99 }
say bar; # 99
&bar = { 100 }
say bar; # 100
如果您想声明一个可变的例程变量并且不允许允许在没有印记的情况下轻松调用它,您可以使用$ 来声明它:
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:< { } > 和postcircumfix:« < > »,但相关的角色/协议是Associative 一致的索引。
类似的情况也适用于postcircumfix:< ( ) > 和Callable。