急需的奇怪行为

Hol*_*lli 6 raku

天生懒惰

D:\>6e "my @bar = 'a', 'b', 'c'; sub foo( @b ) { my $bar = 0; gather loop { print "*"; take ($bar, @b[$bar]); $bar++; last if $bar > 2; } }; .print for foo( @bar )"
*(0 a)*(1 b)*(2 c)
Run Code Online (Sandbox Code Playgroud)

到目前为止,如此预期。现在,让我们热切期待。

D:\>6e "my @bar = 'a', 'b', 'c'; sub foo( @b ) { my $bar = 0; eager gather loop { print "*"; take ($bar, @b[$bar]); $bar++; last if $bar > 2; } }; .print for foo( @bar )"
***(3 a)(3 b)(3 c)
Run Code Online (Sandbox Code Playgroud)

为什么数值都是“3”?好像,我不知道,闭包在一阵不合逻辑的情况下消失了。take-rw也不剪。

D:\>6e "my @bar = 'a', 'b', 'c'; sub foo( @b ) { my $bar = 0; eager gather loop { print "*"; take-rw ($bar, @b[$bar]); $bar++; last if $bar > 2; } }; .print for foo( @bar )"
***(3 a)(3 b)(3 c)
Run Code Online (Sandbox Code Playgroud)

我可以减轻

D:\>6e "my @bar = 'a', 'b', 'c'; sub foo( @b ) { my $bar = 0; eager gather loop { print "*"; take-rw ($bar + 0, @b[$bar]); $bar++; last if $bar > 2; } }; .print for foo( @bar )"
***(0 a)(1 b)(2 c)
Run Code Online (Sandbox Code Playgroud)

但为什么我必须这样做?

编辑:所以,问题归结为,

>6e "my $bar = 0; .say for gather { take ($bar,); $bar++; take ($bar,) }"
(0)
(1)

>6e "my $bar = 0; .say for eager gather { take ($bar,); $bar++; take ($bar,) }"
(1)
(1)
Run Code Online (Sandbox Code Playgroud)

仍然无法解释,为什么在eagerlazy案例中的行为存在差异。

rai*_*iph 6

Holli 已经接受了引用 Brad 两条评论的答案。我特意保持了那个简洁。

但你正在阅读这个。1所以我会用这个答案反其道而行之。

($bar,...)是一个包含$bar, not$bar值的列表

问题中显示的行为实际上完全是关于容器与值,以及列表文字如何存储容器,而不是它们的值1

my $bar = 0;
my $list = ($bar,);
say $list[0];       # 0
$bar = 1;
say $list[0];       # 1
Run Code Online (Sandbox Code Playgroud)

懒惰与渴望的方面只是做他们应该做的事情。强调第一点可能足以激发您关注容器与价值。也许这会引导您快速了解 Holli 的代码中出了什么问题。但也许不是。1所以我会继续。

在懒惰的情况下会发生什么?

惰性列表在尝试生成之前会等待,直到需要一个值。然后,它只是做必要的工作并暂停,让出控制权,直到稍后要求它产生另一个值。

在 Holli 的代码中,for循环需要值。

第一次for循环时,它需要lazy表达式中的一个值。这反过来并要求gather'd 表达式的值。后者然后计算直到take,到那时它创建了一个列表,其第一个元素是container $bar。此列表是take.

然后.print打印第一个列表。印刷时,$bar仍包含0. (第一个增量$bar尚未发生。)

第二次for循环时,重新进入包含take(the loop)的内部控制结构。发生的第一件事是$bar第一次增加。然后检查循环退出条件(并失败),因此第二次循环开始。另一个列表被创建。然后是taked。

当第二个列表被打印时,它的第一个元素,也就是$bar容器,打印为1,而不是0,因为在那个时候,已经增加了,$bar现在包含1

(如果 Holli 编写了保留第一个列表的代码,并且现在再次打印第一个列表,在刚刚打印第二个列表之后,他们会发现第一个列表现在打印了 a ,不再是 a 。因为所有所述d列表具有相同的作为第一元件的容器。)10take $bar

第三个名单也是如此。

打印第三个列表后,for循环要求在gather. 这将loop在语句之后重新输入attake语句。$bar第三次增加到3,然后last if $bar > 2;条件触发,退出循环(因此表达式被gather'd 并最终是整个.print for ...语句)。

在急切情况下会发生什么?

所有gatherING之前完成所有的的printING。

最后,该for构造具有三个列表的序列。它尚未拨打任何.print电话。第三次围绕loopgather已经离开了$bar包含3

接下来,.print在三个列表中的每一个上调用。$bar包含3所以它们都打印3为它们的第一个元素。

通过切换到数组解决问题

我认为处理这个问题的惯用方法是从列表文字切换到数组文字:

[$bar, @bar[$bar]]
# instead of
($bar, @bar[$bar])
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为与列表文字不同,数组文字将作为容器的元素视为r 值2,即它将容器中包含的值复制到该容器之外,而不是存储容器本身。

碰巧该值被复制到另一个 Scalar容器中。(这是因为新的非本地数组的所有元素都是新的Scalar容器;这是使数组与列表不同的主要因素之一。)但是在这种情况下,效果与将值直接复制到数组中的效果相同因为$bar随着事情的进行,其中包含的价值正在发生变化不再重要。

其结果是,三个阵列的第一个元素结束分别含有,01,和2,被包含在这三个值$bar的时候每个阵列被实例化。

通过切换到表达式解决问题

正如 Holli 所指出的,写作$bar + 0也有效。

事实上,任何表达式都可以,只要它不只是$bar靠它自己。

当然,表达式需要工作,并返回正确的值。我认为$bar.self无论$bar绑定或分配什么值都应该工作并返回正确的值。

(虽然它读起来有点奇怪;如果绑定到一个容器,它本身$bar.self不是 !事实上,在一个更违反直觉的扭曲中, even使用,一种“返回底层对象,如果有的话的方法,最终仍然被视为r 值!)$bar$barScalar$bar.VAR.VARScalar

文档需要更新吗?

以上是完全合乎逻辑的结果:

  • 什么Scalar是;

  • 列表文字对Scalars有何作用;

  • 懒惰与急切处理意味着什么。

如果文档薄弱,大概是在对最后两个方面之一的解释中。看起来它主要是列表文字方面。

文档的语法页面有一个关于各种文字的部分,包括数组文字,但不包括列表文字。该文档的Lists、sequence 和 arrays 确实有一个List 文字部分(而不是关于 Arrays 的部分),但它没有提到它们对Scalars 的作用。

想必这值得关注。

列表,序列和阵列页面也有一个懒惰的名单,可以或许被更新部分。

将以上内容放在一起,看起来最简单的文档修复可能是更新列表、序列和数组页面。

脚注

1在我这个答案的前几个版本 ( 1 , 2,我试图让 Holli 反思容器与价值的影响。但这对他们来说失败了,也许对你也没有用。如果你不熟悉使用 Raku 的容器,请考虑阅读:

  • Containers,官方文档的“Raku 容器的低级解释”。

  • Perl 6 中的容器,Elizabeth Mattijsen 为熟悉 Perl 的人撰写的关于 Raku 基础知识的系列文章的第三篇。

2维基百科关于“l 值和 r 值”的讨论中的一些细节不适合 Raku,但一般原则是相同的。


rai*_*iph 1

根据@BradGilbert:

它只是关于容器和价值观。

惰性案例起作用的原因是因为您没有注意到旧值从您的下面发生了变化,因为它们已经被打印了。

请参阅 Brad 和 raiph 的回答以进行进一步讨论。