除非强制列表之一,否则 Raku 列表加法运算符“Z+”“失败”

Jul*_*lio 13 arrays list rakudo raku

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

我有一些我想总结的 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)
Run Code Online (Sandbox Code Playgroud)

现在我们将做同样的事情,但我将使用存储在其他地方的值更改正确的操作数。在这种情况下,我有一个列表数组:

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

这给出了(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)
Run Code Online (Sandbox Code Playgroud)

另一种是将@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)
Run Code Online (Sandbox Code Playgroud)

为什么第一种情况不起作用?

Eli*_*sen 8

如果您+从 中删除Z,并使用dd代替say,它可能会变得更清晰:

dd (3,3) Z @foo[1];  # ((3, $(2, 2)),).Seq
Run Code Online (Sandbox Code Playgroud)

因此,在这种情况下,您会得到一个包含3和的列表(2,2)。请注意:$之前的内容(2,2),这意味着它被逐项列出:被视为单个项目。

现在使用Z+,而不是创建列表,您将添加值。

当你写:

say 3 + (42,666);    # 5
Run Code Online (Sandbox Code Playgroud)

你得到5是因为你将列表中的元素数量添加到3. 这就是为什么你也在5你的例子中结束,而不是因为列表中的值是2.

在其他情况下,Z运算符会看到非逐项列表,因此按预期迭代其元素。

如有疑问,请确保在调试中使用dd而不是say:它会给你一个表达式的本质,而不仅仅是一个“要点”:-)

  • 谢谢!为什么 `((1,1),)[0]` 返回一个非逐项列表,但另一方面,`[(1,1),][0]` 返回一个逐项列表? (3认同)
  • @julio `[ , , ]` 创建一个数组,其中逐项列出了其元素。`( , , )` 创建一个列表,默认情况下不逐项列出 (3认同)

rai*_*iph 5

另一种看待事物的方式......

my @foo = (1,1), (2,2);
say @foo.WHAT;              # (Array)
say @foo[1].WHAT;           # (List)   <== It was already a list, but...
Run Code Online (Sandbox Code Playgroud)

……是吗?

==> 不,它不是。

这在两个方面是您问题的主要关键:

  • 首先,正如 Liz 所指出的,当您试图了解遇到惊喜时发生了什么时,请使用dd,而不是say,因为dd重点关注潜在的现实。

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


了解潜在现实和Scalars的作用的另一种方法是稍微扩展您的示例:

my @foo = (1,1), (2,2);
say @foo.WHAT;              # (Array)
say @foo[1].VAR.WHAT;       # (Scalar) <== In reality it 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`s do *not* make `Scalar`s
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] = ...;              # Fails no matter what `@foo2[1]` and `...` are.
@foo2[1] := ...;             # Fails no matter what `@foo2[1]` and `...` are.
Run Code Online (Sandbox Code Playgroud)

我将提请注意上述几个方面:

  • AScalar通常对自己保持沉默

    AScalar返回它在r 值上下文中包含的值,除非您明确地使用.VAR.

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

    在我写下这个答案之前,我还没有将这方面干净地整合到我对 Raku 使用Scalars 的理解中。也许这是显而易见的人,但我觉得这是这里值得一提,因为Scalar由表示$(...)从显示器dd.raku一个只读一个-你不能分配给它。

  • 一个Array“autovivifies”的读/写Scalar为它的每个元素的

    如果将值分配@foo[42](非本机)的索引位置(例如)Array,则如果该元素当前没有:exist(即@foo[42]:existsFalse),则新的读/写Scalar是“自动激活”(自动创建并绑定到该索引位置)作为处理分配的第一步。

  • AList 从不为其Scalar任何元素自动激活 a

    当一个值被“赋值”(实际上是绑定,即使使用了“赋值”这个词)到 a 中的索引位置时List,不会发生自动激活。AList 可以包括Scalars,包括读/写的,但唯一可能发生的方法是将现有的读/写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
Run Code Online (Sandbox Code Playgroud)

我们将强制数字运算 (+) 应用于列表(Positional值),而不是其元素。强制为数字的列表是它的“长度”(元素数)。(当然对于非稀疏的。我不确定稀疏的。)