gpt4 book ai didi

raku - 急需的奇怪行为

转载 作者:行者123 更新时间:2023-12-04 14:08:16 26 4
gpt4 key购买 nike

天生懒惰

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)

到目前为止,如此预期。现在,让我们热切期待。
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)

为什么数值都是“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)

我可以减轻
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)

但为什么我必须这样做?

编辑:所以,问题归结为,
>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)

仍然无法解释,为什么 eager 中的行为存在差异对比 lazy案件。

最佳答案

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

但是你正在阅读这篇文章。1 所以我会在这个答案中反其道而行之。
($bar,...)是一个包含$bar的列表,而不是$bar的值

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

my $bar = 0;
my $list = ($bar,);
say $list[0]; # 0
$bar = 1;
say $list[0]; # 1

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

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

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

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

第一次围绕 for循环,它需要来自 lazy表达式的值。这反过来并要求来自 gatherd 表达式的值。后者然后计算直到 take,到那时它创建了一个列表,其第一个元素是容器 $bar。此列表是 take的结果。

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

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

当第二个列表被打印时,它的第一个元素,即 $bar容器,打印为 1,而不是 0,因为在这一点上, $bar现在包含 1

(如果 Holli 编写了保留第一个列表的代码,并且现在再次打印第一个列表,在刚刚打印第二个列表后,他们会发现第一个列表现在也打印了 1,不再是 0。因为所有 taked 列表都具有相同的 $bar容器作为它们的第一个元素。)

第三个名单也是如此。

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

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

所有 gathering 都在任何 printing 之前完成。

最后, for构造具有三个列表的序列。它尚未拨打任何 .print电话。第三次围绕 loop中的 gather留下了包含 $bar3

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

通过切换到数组解决问题

我认为处理这个问题的惯用方法是从列表文字切换到数组文字:
[$bar, @bar[$bar]]
# instead of
($bar, @bar[$bar])

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

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

结果是三个数组的第一个元素最终分别包含012,即每个数组实例化时包含在$bar中的三个值。

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

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

事实上,任何表达式都可以,只要它本身不只是$bar

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

(虽然它读起来有点奇怪;如果$bar.self绑定(bind)到$bar容器,$bar本身不是Scalar!事实上,在一个更违反直觉的扭曲中,即使是$bar.VAR,它使用.VAR ,一种"Returns the underlying Scalar object, if there is one."的方法,最终仍被视为 r 值!)

文档需要更新吗?

以上是完全合乎逻辑的结果:
  • Scalar是什么;
  • 列表文字与Scalar有什么关系;
  • 懒惰与急切处理意味着什么。

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

    doc's Syntax page有一个关于各种文字的部分,包括数组文字,但不包括列表文字。 doc's Lists, sequences, and arrays确实有一个列表文字部分(而不是数组),但它没有提到它们对Scalar的作用。

    想必这值得关注。

    列表、序列和数组页面还有一个Lazy lists部分,可能会更新。

    综上所述,看起来最简单的文档修复可能是更新列表、序列和数组页面。

    脚注

    1 在我这个答案的前几个版本(12)中,我试图让 Holli 反射(reflection)容器与值(value)的影响。但这对他们来说失败了,也许对你也不起作用。如果你不熟悉 Raku 的容器,请考虑阅读:
  • Containers,官方文档的“Raku容器低级解释”。
  • Containers in Perl 6,Elizabeth Mattijsen 为熟悉 Perl 的人撰写的关于 Raku 基础知识的系列文章的第三篇。

  • 2 Wikipedia's discussion of "l-values and r-values"中的一些细节不适合Raku,但大体原理是相同的。

    关于raku - 急需的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59739980/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com