Raku 列表添加运算符"Z+"'fails'除非其中一个列表被强制



我很难理解为什么zip addZ+运算符在某些情况下不起作用。

我有一些两个元素的清单,我想总结一下。

无论我使用列表还是数组,这些都能正常工作:

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)的意外结果(至少对我来说:(。

有几种方法可以解决这个问题。

第一个是强制got元素为列表:

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关注的是潜在的现实。

  • 其次,重要的是要了解Scalars在Raku中的作用,以及这是如何将Arrays与Lists区分开来的。


了解潜在现实和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通常对自己保持沉默

    Scalar返回r值上下文中包含的值,除非您使用.VAR显式查找它。

  • Scalar容器可以读/写或只读

    在我写下这个答案之前,我还没有将这一方面清晰地融入到我对Raku使用Scalars的理解中。也许这对其他人来说是显而易见的,但我觉得这里值得一提,因为dd.raku中的$(...)显示所指示的Scalar是只读的——你不能分配给它。

  • CCD_ 38〃;自动激活";(自动创建并绑定(其每个元素的读/写Scalar

    如果值被分配到(非本地(Array的索引位置(比如@foo[42](,则如果该元素当前不:exist(即@foo[42]:existsFalse(,则新的读/写Scalar是"0";自动激活";作为处理分配的第一步。

  • Listnever使Scalar的任何元素自动激活

    当一个值是"0"时;指定";(实际上绑定,即使使用单词"assigned"(到List中的索引位置,也不会发生自动生动化。List可以包括Scalars,包括读/写,但唯一可能发生的方式是如果现有的读/写Scalar是"0";指定";到元素(索引位置(,例如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);

Do:

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)  

最新更新