给定单个参数,Array
构造函数将其展平。这会导致问题:
my %hash = (a => 1; b => 2);
my @array = [ %hash ]; # result: [a => 1 b => 2], expected [{ a => 1, b => 2 }]
List 构造函数没有这个怪癖(单参数规则),但不幸的是,没有用于创建单元素列表的简短语法:
List.new(%hash); # result is ({a => 1, b => 2}), as expected
解决方法:如果您的参数是标量,它不会自动展平:
my $hash = %hash;
my @array = [ $%hash ]; # or my @array = [ $%hash ], or just my @array = $%hash
# result: [{a => 1, b => 2}], as expected
另一种解决方法是在元素列表的末尾添加一个逗号:
my @array = [ %hash, ];
真正的问题是当我们从字面上写出数据时。在 Perl 6 中表示类似 JSON 的嵌套结构是一个真正的问题,如果 1 元素列表是扁平的,但其他列表不是。数据最终是错误的。使用MongoDB时,我不得不写出大量数据,因为MongoDB API参数必须格式化为嵌套列表/数组。这几乎是不可能的。所以我问,扁平化单个数组元素的动机是什么?
扁平化单个数组元素的动机是始终如一地应用单个参数规则。 数组构造函数[ ]
也遵循单参数规则。 也许将[%h]
视为circumfix:<[ ]>(%h)
会有所帮助,实际上确实如此。 如果您不想拼合,则可以逐项列出它(前缀为$
),或者如您所显示的,添加一个逗号以使其成为List
。 这遵循相同的逻辑,($a)
与$a
相同,但($a,)
是一个$a
元素的List
。
my %h = a => 42, b => 666;
dd [%h]; # [:a(42), :b(666)]
dd [%h,%h]; # [{:a(42), :b(666)}, {:a(42), :b(666)}]
dd [%h,] # [{:a(42), :b(666)},] make it a List first
dd [$%h] # [{:a(42), :b(666)},] itemize the Hash
使用 1-arg 规则的数组构造运算符的动机:
- 因为它实际上只是一个环形运算符,而不是特殊的语法。
其他期望潜在嵌套列表作为输入的运算符/关键字使用单参数规则 - 因此为了保持一致性,这个规则也是如此。
通常使用 1-arg 规则进行列表转换运算符/关键字的动机:
- 为了方便整个输入列表已经存储在一个变量中的常见情况。
- 以避免进一步的特殊大小写文本逗号。运算符
没有像函数那样的参数列表;它们只接受一个对象作为它们的参数(一元/环形运算符),或者每边一个(二元运算符)。 表达式@a,
构造一个单元素List
,因此当将该表达式的结果传递给列表转换运算符时,它会像对待传递给它的任何其他非容器化Iterable
一样对待该List
:它迭代它,并对其元素进行操作。
事实上 - ,当参数被包装在项目容器(即
$
变量)中时,它不会被迭代,这是为了保留符号的单复数区别,正如@p6steve的答案所解释的那样。
下面是上述一致性,使用一个关键字、一个二进制运算符和一个环形运算符演示:
for @a { ... } # n iterations
for @a, { ... } # 1 iteration
for @a, @b { ... } # 2 iterations
for $a { ... } # 1 iteration
1..Inf Z @a # new Seq of n elements
1..Inf Z @a, # new Seq of 1 element
1..Inf Z @a, @b # new Seq of 2 elements
1..Inf Z $a # new Seq of 1 element
[ @a ] # new Array with n elements
[ @a, ] # new Array with 1 element
[ @a, @b ] # new Array with 2 elements
[ $a ] # new Array with 1 element
子例程/方法呢?
它们确实有参数列表,因此单参数规则对它们来说并不像运算符那样自然。
请注意,在参数列表的顶级范围内,逗号不会创建List
- 它们分隔参数。
被视为"列表转换"例程的子例程/方法期望潜在嵌套列表作为输入,仍然参与单参数规则,方法是检查它们是否得到一个参数或多个参数,如果只有一个参数,则是否包装在项目容器中:
map {...}, @a; # n iterations
map {...}, (@a,); # 1 iteration (Parens needed to get a List-constructing comma.)
map {...}, @a, @b; # 2 iterations
map {...}, $a; # 1 iteration
用户定义的例程可以使用+@
签名轻松获取此行为。
Perl 中的 @ 符号表示"这些",而 $ 表示"the"。这种复数/单数的区别出现在语言的不同地方,Perl 中的便利也由此而来。扁平化是指在某些情况下,类似 @ 的事物会自动将其值合并到周围的列表中。传统上,这一直是 Perl 中强大力量和巨大混乱的来源。Perl 6 在演变过程中经历了许多与扁平化相关的模型,然后才确定了一个称为"单参数规则"的简单模型。
通过考虑 for 循环将执行的迭代次数来最好地理解单参数规则。要迭代的内容始终被视为 for 循环的单个参数,因此是规则的名称。
for 1, 2, 3 { } # List of 3 things; 3 iterations
for (1, 2, 3) { } # List of 3 things; 3 iterations
for [1, 2, 3] { } # Array of 3 things (put in Scalars); 3 iterations
for @a, @b { } # List of 2 things; 2 iterations
for (@a,) { } # List of 1 thing; 1 iteration
for (@a) { } # List of @a.elems things; @a.elems iterations
for @a { } # List of @a.elems things; @a.elems iterations
从剧情简介7 https://design.raku.org/S07.html#The_single_argument_rule