由 deepmap 创建的数组中的不可变(非容器化)元素

Nik*_*nes 12 raku

如果我正确理解 Raku 文档,数组的元素总是容器化的,即标量。然而, deepmap 方法似乎创建了具有非容器化元素的(内部)数组:

my @a = [1, [2, 3]];
my @b = @a.deepmap: *.clone;
say @b[0].VAR.^name;     # Scalar, this is OK
say @b[1].^name;         # Array, as expected
say @b[1][0].VAR.^name;  # Int, why?
@b[0] = 4;               # this works
@b[1][0] = 5;            # error: Cannot assign to an immutable value
Run Code Online (Sandbox Code Playgroud)

为什么会出现这种情况?

对于上下文,我最初想用来.deepmap: *.clone创建深层副本,但我需要副本是可变的。我通过使用解决了这个问题@a.deepmap: { my $ = .clone },但我仍然很好奇为什么会发生这种情况。

cod*_*ons 10

\n

如果我正确理解 Raku 文档,数组的元素总是容器化的,即标量。

\n
\n

几乎是正确的,但不完全是 \xe2\x80\x93 数组初始化(即使用[1, 2])将值容器化,但这并不意味着元素总是容器化的。例如,您可以显式绑定一个值到数组中的位置。

\n

或者,正如您所发现的,当以不寻常的方式创建数组时,您可能会得到一个非容器化的值。让我们看看是什么deepmap这里做了什么:

\n
my @a = [1, [2, 3]];\n\n@a.deepmap({.say; $_});  # OUTPUT: \xc2\xab1\xe2\x90\xa42\xe2\x90\xa43\xe2\x90\xa4\xc2\xbb\nsay @a.raku;             # OUTPUT: \xc2\xab[1 [2 3]]\xc2\xbb\n
Run Code Online (Sandbox Code Playgroud)\n

这是怎么回事?好吧,deepmap是递归地下降到结构中并在每个叶元素上调用函数(这就是它打印2而不是[2 3]在第二次迭代时打印的原因)。然后就绑定了到它正在迭代的槽。

\n

因此,随着.clone,deepmap向下到达叶子(例如2)并调用.clone该值,得到2(一个Int ) 并将其绑定到数组中的位置。

\n

看来你想要发生的事情是为了.clone被召唤[2 3],而不是2。如果是这样,您可以对具有一级嵌套的列表(如上)执行此操作.map(*.clone);对于更复杂的嵌套,您可以在表达式duckmap内使用或测试map(或者,正如您所发现的,调用.clone叶值并添加Scalar。)

\n

  • 嗯,是的,数组文档中的“强制”语言有点不幸,值得更改。回复:“为什么 @b[0] 是一个标量”——这是因为“@”符号。请注意,“@a.deepmap(*.clone)[0].VAR.^name”返回“Int”(没有“Scalar”)。但是,当您将“deepmap”的结果分配给“@b”时,您会得到“@”的默认行为:复制右侧的每个元素并将其分配给绑定到左侧相应位置的标量。(请注意,如果您使用“my @b is List =”声明“@b”,则不会获得“$b[0]”的标量(或“@b[0]”的标量)。) (3认同)
  • 文档说:“数组是一个列表,它强制其所有元素成为标量容器”。似乎作者以一种非常不寻常的方式使用“force”这个词......此外,文档还说“强烈建议不要将数组槽直接绑定到值”。令我惊讶的是,标准方法做了一些强烈不鼓励的事情。 (2认同)