Raku 列表加法运算符 `Z+` 'fails' 除非强制列表之一
Raku list addition operator `Z+` 'fails' unless one of the lists is forced
我很难理解为什么 zip-add Z+
运算符在某些情况下不起作用。
我有一些我想要求和的 2 元素列表。
无论我使用列表还是数组,这些都按预期工作:
say (1, 2) Z+ (3, 4) # (4, 6)
say [1, 2] Z+ (3, 4) # (4, 6)
say [1, 2] Z+ [3, 4] # (4, 6)
say (1, 2) Z+ [3, 4] # (4, 6)
现在我们将做同样的事情,但我将用存储在别处的值更改正确的操作数。在这种情况下,我有一个列表数组:
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List)
say @foo[1]; # (2,2)
say (3,3) Z+ @foo[1]; # (5) ???
这给出了 (5)
.
的意外结果(至少对我来说 :))
有几种方法可以解决这个问题。
第一个是强制获取的元素是一个列表:
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List) <== It was already a list, but...
say @foo[1]; # (2,2)
say (3,3) Z+ @foo[1].list; # <== changed. (5,5)
另一个是将 @foo
定义更改为列表而不是数组(通过 is List
或通过绑定 :=
值)
my @foo is List = (1,1), (2,2); # <=== Changed
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List) <== It was already a list
say @foo[1]; # (2,2)
say (3,3) Z+ @foo[1]; # (5,5)
为什么第一个案例不起作用?
如果把Z
中的+
去掉,用dd
代替say
,可能会更清楚:
dd (3,3) Z @foo[1]; # ((3, $(2, 2)),).Seq
所以在这种情况下,您会得到一个包含 3
和 (2,2)
的列表。请注意 (2,2)
之前的 $
:这意味着它被逐项列出:被视为单个项目。
现在 Z+
,您将添加 值而不是创建列表。
当你写:
say 3 + (42,666); # 5
您得到 5
,因为您将 列表中的元素数 添加到 3
。这就是为什么你在你的例子中也以 5
结尾, 而不是 因为列表中的值是 2
.
在其他情况下,Z
运算符会看到非逐项列表,因此 将 像您预期的那样迭代其元素。
如有疑问,请确保在调试中使用 dd
而不是 say
:它将为您提供表达式的细节,而不仅仅是“要点”:- )
另一种看待事物的方式...
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List) <== It was already a list, right?
==> 不,不是。
这在两个方面是您问题的主键:
首先,正如 Liz 所说,当你试图理解遇到意外时发生的事情时,请使用 dd
,而不是 say
,因为 dd
专注于关于潜在的现实。
其次,重要的是要了解 Scalar
s 在 Raku 中的作用,以及它如何明确区分 Array
s 和 List
s。
了解底层现实和 Scalar
作用的另一种方法是稍微扩展您的示例:
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array) <== Top level elements "autovivify" as `Scalar`s
say @foo[1].VAR.WHAT; # (Scalar) <== The element was a `Scalar`, not a `List`
say @foo[1].WHAT; # (List) <== The `Scalar` returns the value it contains
@foo[1] = 42; # Works. <== The `Scalar` supports mutability
my @foo2 is List = (1,1), (2,2);
say @foo2.WHAT; # (List) <== `List` elements *don't* "autovivify"
say @foo2[1].VAR.WHAT; # (List) <== `VAR` on a non-`Scalar` is a no op
say @foo2[1].WHAT; # (List) <== This time `@foo2[1]` IS a *`List`*
@foo2[1] = ...; # Attempt to assign to `List` bound to `@foo2[1]` fails
@foo2[1] := ...; # Attempt to bind to `@foo2[1]` element fails
我会提请注意以上几个方面:
一个Scalar
通常对自己保持沉默
A Scalar
returns 它包含在 r-value 上下文中的值,除非您明确地使用 .VAR
.
查找它
Scalar
容器可以是 read/write 或 readonly
在我写这个答案之前,我还没有把这方面干净利落地融入到我对 Raku 使用 Scalar
的理解中。也许这对其他人来说很明显,但我觉得这里值得一提,因为 dd
和 .raku
中的 $(...)
显示所指示的 Scalar
是只读的 - 你不能分配给它。
一个Array
“自动生成”(自动创建和绑定)一个read/writeScalar
为其每个元素
如果一个值分配到(非本地)Array
的索引位置(比如@foo[42]
),那么如果该元素确实不是当前:exist
(即@foo[42]:exists
是False
),然后一个新鲜的read/writeScalar
被“自动激活”为处理作业的第一步。
A List
从不 自动生成 Scalar
的任何元素
当一个值被“赋值”(实际上是绑定,即使使用了“赋值”一词)到 List
中的索引位置时,没有自动生成曾经发生过。 List
可以包括Scalar
,包括read/write,但唯一可能发生的方法是现有 read/write Scalar
被“分配”给一个元素(索引位置),例如 my @foo := (42, $ = 99); @foo[1] = 100; say @foo; # (42 100)
.
现在我们可以理解你的代码了 (5)
:
my @foo = (1,1), (2,2); # `@foo` is bound to a fresh non-native `Array`
say @foo[1].VAR.WHAT; # (Scalar) -- @foo[1] is an autovivified `Scalar`
say @foo[1]; # (2,2) -- `say` shows value contained by `Scalar`
say (3,3) Z+ @foo[1]; # (5) --- because it's same as follows:
say +$(2,2); # 2 -- number of elements in a two element list †
say (3,3) Z+ 2; # (5) -- `Z` stops if either side exhausted
† 我们正在对列表 (Positional
) 应用强制数字运算 (+
)值),而不是它的元素。强制为数字的列表是它的“长度”(元素数)。 (当然是非稀疏的。我不确定稀疏的。)
开始于:
my @foo = (1,1), (2,2);
做:
say (3,3) Z+ @foo[1][*]; # (5 5)
或
say (3,3) Z+ @foo[1][]; # (5 5)
或
say (3,3) Z+ @foo[1]<>; # (5 5)
或
say (3,3) Z+ @foo[1]:v; # (5 5)
我很难理解为什么 zip-add Z+
运算符在某些情况下不起作用。
我有一些我想要求和的 2 元素列表。
无论我使用列表还是数组,这些都按预期工作:
say (1, 2) Z+ (3, 4) # (4, 6)
say [1, 2] Z+ (3, 4) # (4, 6)
say [1, 2] Z+ [3, 4] # (4, 6)
say (1, 2) Z+ [3, 4] # (4, 6)
现在我们将做同样的事情,但我将用存储在别处的值更改正确的操作数。在这种情况下,我有一个列表数组:
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List)
say @foo[1]; # (2,2)
say (3,3) Z+ @foo[1]; # (5) ???
这给出了 (5)
.
有几种方法可以解决这个问题。
第一个是强制获取的元素是一个列表:
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List) <== It was already a list, but...
say @foo[1]; # (2,2)
say (3,3) Z+ @foo[1].list; # <== changed. (5,5)
另一个是将 @foo
定义更改为列表而不是数组(通过 is List
或通过绑定 :=
值)
my @foo is List = (1,1), (2,2); # <=== Changed
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List) <== It was already a list
say @foo[1]; # (2,2)
say (3,3) Z+ @foo[1]; # (5,5)
为什么第一个案例不起作用?
如果把Z
中的+
去掉,用dd
代替say
,可能会更清楚:
dd (3,3) Z @foo[1]; # ((3, $(2, 2)),).Seq
所以在这种情况下,您会得到一个包含 3
和 (2,2)
的列表。请注意 (2,2)
之前的 $
:这意味着它被逐项列出:被视为单个项目。
现在 Z+
,您将添加 值而不是创建列表。
当你写:
say 3 + (42,666); # 5
您得到 5
,因为您将 列表中的元素数 添加到 3
。这就是为什么你在你的例子中也以 5
结尾, 而不是 因为列表中的值是 2
.
在其他情况下,Z
运算符会看到非逐项列表,因此 将 像您预期的那样迭代其元素。
如有疑问,请确保在调试中使用 dd
而不是 say
:它将为您提供表达式的细节,而不仅仅是“要点”:- )
另一种看待事物的方式...
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array)
say @foo[1].WHAT; # (List) <== It was already a list, right?
==> 不,不是。
这在两个方面是您问题的主键:
首先,正如 Liz 所说,当你试图理解遇到意外时发生的事情时,请使用
dd
,而不是say
,因为dd
专注于关于潜在的现实。其次,重要的是要了解
Scalar
s 在 Raku 中的作用,以及它如何明确区分Array
s 和List
s。
了解底层现实和 Scalar
作用的另一种方法是稍微扩展您的示例:
my @foo = (1,1), (2,2);
say @foo.WHAT; # (Array) <== Top level elements "autovivify" as `Scalar`s
say @foo[1].VAR.WHAT; # (Scalar) <== The element was a `Scalar`, not a `List`
say @foo[1].WHAT; # (List) <== The `Scalar` returns the value it contains
@foo[1] = 42; # Works. <== The `Scalar` supports mutability
my @foo2 is List = (1,1), (2,2);
say @foo2.WHAT; # (List) <== `List` elements *don't* "autovivify"
say @foo2[1].VAR.WHAT; # (List) <== `VAR` on a non-`Scalar` is a no op
say @foo2[1].WHAT; # (List) <== This time `@foo2[1]` IS a *`List`*
@foo2[1] = ...; # Attempt to assign to `List` bound to `@foo2[1]` fails
@foo2[1] := ...; # Attempt to bind to `@foo2[1]` element fails
我会提请注意以上几个方面:
一个
Scalar
通常对自己保持沉默A
查找它Scalar
returns 它包含在 r-value 上下文中的值,除非您明确地使用.VAR
.Scalar
容器可以是 read/write 或 readonly在我写这个答案之前,我还没有把这方面干净利落地融入到我对 Raku 使用
Scalar
的理解中。也许这对其他人来说很明显,但我觉得这里值得一提,因为dd
和.raku
中的$(...)
显示所指示的Scalar
是只读的 - 你不能分配给它。一个
Array
“自动生成”(自动创建和绑定)一个read/writeScalar
为其每个元素如果一个值分配到(非本地)
Array
的索引位置(比如@foo[42]
),那么如果该元素确实不是当前:exist
(即@foo[42]:exists
是False
),然后一个新鲜的read/writeScalar
被“自动激活”为处理作业的第一步。A
List
从不 自动生成Scalar
的任何元素当一个值被“赋值”(实际上是绑定,即使使用了“赋值”一词)到
List
中的索引位置时,没有自动生成曾经发生过。List
可以包括Scalar
,包括read/write,但唯一可能发生的方法是现有 read/writeScalar
被“分配”给一个元素(索引位置),例如my @foo := (42, $ = 99); @foo[1] = 100; say @foo; # (42 100)
.
现在我们可以理解你的代码了 (5)
:
my @foo = (1,1), (2,2); # `@foo` is bound to a fresh non-native `Array`
say @foo[1].VAR.WHAT; # (Scalar) -- @foo[1] is an autovivified `Scalar`
say @foo[1]; # (2,2) -- `say` shows value contained by `Scalar`
say (3,3) Z+ @foo[1]; # (5) --- because it's same as follows:
say +$(2,2); # 2 -- number of elements in a two element list †
say (3,3) Z+ 2; # (5) -- `Z` stops if either side exhausted
† 我们正在对列表 (Positional
) 应用强制数字运算 (+
)值),而不是它的元素。强制为数字的列表是它的“长度”(元素数)。 (当然是非稀疏的。我不确定稀疏的。)
开始于:
my @foo = (1,1), (2,2);
做:
say (3,3) Z+ @foo[1][*]; # (5 5)
或
say (3,3) Z+ @foo[1][]; # (5 5)
或
say (3,3) Z+ @foo[1]<>; # (5 5)
或
say (3,3) Z+ @foo[1]:v; # (5 5)