为什么Perl 6数组构造函数会使其参数变得平坦?

pio*_*ojo 12 perl6 raku

给定一个参数,Array构造函数将其展平.这会导致问题:

my %hash = (a => 1; b => 2);
my @array = [ %hash ]; # result: [a => 1 b => 2], expected [{ a => 1, b => 2 }]
Run Code Online (Sandbox Code Playgroud)

List构造函数没有这个怪癖(单参数规则),但遗憾的是,创建单元素列表没有简短的语法:

List.new(%hash); # result is ({a => 1, b => 2}), as expected
Run Code Online (Sandbox Code Playgroud)

解决方法:如果您的参数是标量,则不会自动展平:

my $hash = %hash;
my @array = [ $%hash ]; # or my @array = [ $%hash ], or just my @array = $%hash
# result: [{a => 1, b => 2}], as expected
Run Code Online (Sandbox Code Playgroud)

另一种解决方法是在元素列表的末尾添加逗号:

my @array = [ %hash, ];
Run Code Online (Sandbox Code Playgroud)

真正的问题是我们按字面意思写出数据.如果单元素列表被展平,那么在Perl 6中表示类似JSON的嵌套结构是一个真正的问题,但其他列表则不是.数据最终是错误的.使用MongoDB时我必须写出大量数据,因为MongoDB API参数必须格式化为嵌套列表/数组.这几乎是不可能的.所以我问,扁平化单个数组元素的动机是什么?

Eli*_*sen 9

展平单个数组元素的动机是始终如一地应用单个参数规则.数组构造函数[ ] 遵循单个参数规则.也许它有助于觉得[%h]作为infix:<[ ]>(%h),其实际的.如果您不想展平,可以将其逐项列出(前缀a $),或者如您所示,添加逗号以使其成为a List.然后在此之前相同的逻辑($a)是相同$a的,但($a,)作为一个List具有一个元件$a.

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
Run Code Online (Sandbox Code Playgroud)

  • @piojo我认为部分原因是人们在写'我的@array = [1,2,3];`而不是期望`@ array`等同于`[[1,2,3],]`.那是'我的@array = [%hash];`两次应用单个arg规则. (2认同)

p6s*_*eve 7

Perl中的@sigil表示"这些",而$表示"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
Run Code Online (Sandbox Code Playgroud)

来自Synopsis7 https://design.perl6.org/S07.html#The_single_argument_rule


sml*_*mls 5

使用1-arg规则的阵列构造运算符的动机:

  • 因为它实际上只是一个外设运算符,而不是特殊的语法.
    其他运算符/关键字期望一个可能嵌套的列表作为输入,使用单参数规则 - 所以为了保持一致性,这个也是如此.

使用1-arg规则的列表转换运算符/关键字的动机:

  • 为了便于整个输入列表已经存储在一个变量中的常见情况.
  • 为了避免进一步的特殊套管文字逗号.
    运算符没有像函数那样的参数列表; 他们只接受一个对象作为他们的参数(一元/外接运算符),或者每一方都接受一个(二元运算符).该表达式@a,构造一个单元素List,因此当将该表达式的结果传递给列表转换运算符时,它会List像处理Iterable传递给它的任何其他非容器化一样对待它:它迭代它,并对其元素进行操作( S).
  • 当包含在项容器(即变量)中时,参数不会被迭代,这$意味着保留了sigils的单数复数区别,正如@ 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
Run Code Online (Sandbox Code Playgroud)

子程序/方法怎么样?

这些确实有参数列表,因此单参数规则并不像它对运算符那样自然.

请注意,在参数列表的顶级范围中,逗号不会创建Lists - 它们分隔参数.

子程序/方法被认为是"列表转换"例程,它们期望潜在嵌套列表作为输入,但是仍然参与单一规则规则,通过检查它们是否有一个参数或多个,如果只有一个,是否包含在物品容器:

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
Run Code Online (Sandbox Code Playgroud)

用户定义的例程可以通过使用+@签名轻松获得此行为.