【发布时间】:2021-11-12 11:55:45
【问题描述】:
我知道列表赋值会使其左侧变平:
my ($a, $b, $c);
($a, ($b, $c)) = (0, (1.0, 1.1), 2);
say "\$a: $a"; # OUTPUT: «$a: 0»
say "\$b: $b"; # OUTPUT: «$b: 1 1.1» <-- $b is *not* 1
say "\$c: $c"; # OUTPUT: «$c: 2» <-- $c is *not* 1.1
我也明白我们可以使用:($a, ($b, $c)) := (0, (1.0, 1.1)) 来获得非扁平化行为。
我不明白的是为什么在列表分配过程中左侧会变平。这是两个问题:首先,这种扁平化行为如何与语言的其他部分相适应?其次,如果左侧不展平,自动展平是否允许任何不可能的行为?
关于第一个问题,我知道 Raku 历来有很多自动扁平化行为。在Great List Refactor 之前,像my @a = 1, (2, 3), 4 这样的表达式会自动展平其右侧,从而生成数组[1, 2, 3, 4];同样,map 和许多其他迭代构造会使它们的论点变平。然而,在 GLR 之后,Raku 基本上不会在没有被告知的情况下将列表变平。事实上,我想不出在没有flat、.flat、| 或*@ 以某种方式参与的情况下,Raku 会变平的任何其他情况。 (@_ 创建一个隐含的*@)。我是否遗漏了什么,或者列表分配中的 LHS 行为真的与后 GLR 语义不一致?这种行为是历史上的怪事,还是仍然有意义?
关于我的第二个问题,我怀疑列表分配的扁平化行为可能有助于支持懒惰。例如,我知道我们可以使用列表赋值来使用惰性列表中的某些值,而无需全部生成/计算它们——而将:= 与列表一起使用将需要计算所有 RHS 值。但我不确定是否/如何自动展平 LHS 来支持这种行为。
我还想知道自动展平是否与 = 可以传递给元运算符这一事实有关 - 不像 :=,如果与元运算符一起使用会产生“过于繁琐”的错误。但我不知道自动展平如何/是否使 = 不那么“繁琐”。
[编辑:早在2015-05-02,我就发现 IRC 对“(GLR 保留的)列表分配正在扁平化的决定”的引用,因此很明显,这个决定是故意的并且是有充分理由的。但是,到目前为止,我还没有找到这个理由,并且怀疑它可能是在面对面的会议上决定的。所以我希望有人知道。]
最后,我还想知道 LHS 在概念层面上是如何扁平化的。 (我并不是指具体的 Rakudo 实现;我的意思是作为一种心理模型)。以下是我一直在考虑绑定与列表分配的方式:
my ($a, :$b) := (4, :a(2)); # Conceptually similar to calling .Capture on the RHS
my ($c, $d, $e);
($c, ($d, $e) = (0, 1, 2); # Conceptually similar to calling flat on the LHS
除了实际上在第 1 行的 RHS 上调用 .Capture 有效,而在第 3 行的 LHS 上调用 flat 会引发 Cannot modify an immutable Seq 错误 - 我觉得这很令人困惑,因为我们一直在压扁Seqs。那么有没有更好的思维模型来思考这种自动扁平化行为?
提前感谢您的帮助。作为对improve the related docs 的工作的一部分,我正在努力更好地理解这一点,因此您可以提供的任何见解都会支持这项工作。
【问题讨论】:
-
“(GLR 保留的)列表分配扁平化的决定”。看看这个例子,我很确定 jnthn 是在谈论 RHS 被展平,而不是左边。
-
为感兴趣的人发布 REPL 代码和返回值。如果没有预先声明
my ($a, $b, $c);(带或不带括号),您会得到以下结果:> my ($a, $b, $c) = (0, (1.0, 1.1), 2);返回(0 (1 1.1) 2);而> my (($a, $b), $c) = (0, (1.0, 1.1), 2);或my ($a, ($b, $c)) = (0, (1.0, 1.1), 2);返回(0 (1 1.1))。 [$*VM is moar (2021.06)]. -
预声明
my ($a, $b, $c);你会看到以下内容:> my ($a, $b, $c);($a, $b, $c) = (0, (1.0, 1.1), 2);返回(0 (1 1.1) 2)(与没有预声明相同),但是> my ($a, $b, $c);(($a, $b), $c) = (0, (1.0, 1.1), 2);返回((0 (1 1.1)) 2)和 @987654355 @ 返回(0 ((1 1.1) 2))。 [$*VM is moar (2021.06)]. -
@jubilatious1 您上面提到的返回值是正确的,但可能具有误导性。它们是正确的,因为它们是 REPL 打印的内容,并且是赋值表达式的 result。但请注意,当 REPL 打印该结果时,它仍被分组为一个带括号的 single 表达式。但是
$a、$b和$c的值在这三种情况下都是一样的! -
这就是我在 REPL 中看到的带有预声明的内容:
> my ($a, $b, $c);(($a, $b), $c) = (0, (1.0, 1.1), 2);返回((0 (1 1.1)) 2),并逐个遍历变量给出:> say $a;返回0,> say $b;返回 @ 987654365@ 和> say $c;返回2。
标签: variable-assignment raku assignment-operator destructuring flatten