如何正确地对 Pair 对象的值侧进行解容器

Sil*_*olo 8 raku raku-container

我正在Pair循环中构建多个对象,并且对每个对象的值使用相同的标量变量(尽管具有不同的值)。

\n

作为我正在做的事情的简化示例,请考虑

\n
my @list;\nmy $acc = \'\';\n\nfor 1..30 -> $i {\n    if $i % 5 == 4 {\n        @list.push($i => $acc);\n        $acc = \'\';\n    } else {\n        $acc = "$acc $i";\n    }\n}\n\nsay @list;\n
Run Code Online (Sandbox Code Playgroud)\n

(当然,我的实际代码更复杂,并且从文件而不是预定义范围中读取,因此我不能像理论上那样完全消除循环)

\n

我们累积包含写出的数字序列的字符串,创建一个将某些数字映射到低于该数字的值序列的对。

\n

希望这个程序的输出是

\n
[4 =>  1 2 3 9 =>  5 6 7 8 14 =>  10 11 12 13 19 =>  15 16 17 18 24 =>  20 21 22 23 29 =>  25 26 27 28]\n
Run Code Online (Sandbox Code Playgroud)\n

但是,我目前得到

\n
[4 =>  30 9 =>  30 14 =>  30 19 =>  30 24 =>  30 29 =>  30]\n
Run Code Online (Sandbox Code Playgroud)\n

据我了解,这是因为Pair当我将标量分配给其值字段时保留容器,因此我实际上创建了六对,它们的所有值都指向同一个(可变)容器。

\n

文档表明了这一点,甚至还提出了一种解决方法

\n
\n

值得注意的是,当将标量分配为 Pair 的值时,该值保存该值本身的容器。这意味着可以从货币对本身外部更改该值:

\n

...

\n

可以改变上述行为,强制 Pair 删除标量容器并通过冻结方法保留有效值本身

\n
\n

这有效。如果我替换@list.push($i => $acc)

\n
my $pair = ($i => $acc);\n$pair.freeze;\n@list.push($pair);\n
Run Code Online (Sandbox Code Playgroud)\n

然后代码会产生预期的输出。问题是,freeze已弃用,弃用警告下列出的唯一可能替代的代码是

\n
\n
$p.=Map.=head.say;                                    # OUTPUT: \xc2\xaborange\xe2\x90\xa4\xc2\xbb \n
Run Code Online (Sandbox Code Playgroud)\n
\n

看起来它正在将 a 转换Pair为 a Map,然后再转换回来进行某种浅复制。不幸的是,这似乎不起作用,因为@list.push(($i => $acc).Map.head);会产生原始(不正确的)输出。

\n

那么,既然Pair.freeze它显然已被弃用,那么现在在 Raku 中对对象的值大小进行去容器化的正确方法是什么?Pair

\n

p6s*_*eve 9

你们非常接近。

为了了解发生了什么,我将这一行放在print "$i: "; dd @list;for 循环结束之前。

这是一个示例:

19: Array @list = [4 => "", 9 => "", 14 => "", 19 => ""]
20: Array @list = [4 => " 20", 9 => " 20", 14 => " 20", 19 => " 20"]
21: Array @list = [4 => " 20 21", 9 => " 20 21", 14 => " 20 21", 19 => " 20 21"]
Run Code Online (Sandbox Code Playgroud)

因此,正如您所说,问题在于 $acc 容器只是被重用。在您的情况下,您需要将 Pair 值设置为 的内容$acc,而不是容器本身。

这些变体中的任何一个都可以代替您的推线:

@list.push($i => $acc<>);
@list.push($i => "$acc");
Run Code Online (Sandbox Code Playgroud)

decont<>运算符显式取消$acc 容器内容的容器化。

或者,也许更熟悉的是,""引号会生成一个新的 Str 值以及 $acc 当前值的副本。

  • 还有一个文档错误报告,因为我认为搜索“decont”应该是可能的,而无需提前知道它“&lt;&gt;” (3认同)
  • 好的 - 我添加了一个问题来取消冻结 https://github.com/rakudo/rakudo/issues/5121 (2认同)