关于懒惰 [ RAKU ]

jak*_*kar 7 arrays lazy-evaluation rakudo lazy-sequences raku

在 Raku 文档中指出,gather-take 构造正在被惰性评估。在下面的例子中,我很难对结构的懒惰得出结论:

say 'Iterate to Infinity is : ', (1 ... Inf).WHAT;

say 'gather is : ', gather {
    take 0;
    my ($last, $this) = 0, 1;

    loop {
        take $this;
        ($last, $this) = $this, $last + $this;
    }
}.WHAT;

say '------------------------------------';

my @f1 = lazy gather {
    take 0;
    my ($last, $this) = 0, 1;

    loop {
        take $this;
        ($last, $this) = $this, $last + $this;
    }
}

say '@f1         : ', @f1.WHAT;
say '@f1 is lazy : ', @f1.is-lazy;

say '------------------------------------';

my @f2 = 1 ... Inf;

say '@f2         : ', @f2.WHAT;
say '@f2 is lazy : ', @f2.is-lazy;
Run Code Online (Sandbox Code Playgroud)

在第一种情况下(将 Seq 分配给@f1),如果我们去掉“懒惰”定义,那么生成的序列(使用gather-take)将永远运行(不是懒惰)。

在第二种情况下(将 Seq 分配给 @f2)@f2 变得懒惰。

为什么我们在行为上有差异?尽管我们尝试做同样的事情:以懒惰的方式将 Seq 分配给数组

有人能澄清一下吗???

rai*_*iph 9

在 Raku 文档中指出,gather-take 构造正在被惰性评估。

他们可以。但不一定。他们非常乐意以任何一种方式工作。

关键gather是,如果 a被问到它是否是惰性的,它会返回False。这就是您看到的行为的结果。

采取#1:一种方式gather表现得懒惰

gather在需要时评估其序列中的下一个元素。这是懒惰评估的经典定义,如维基百科所述

惰性求值 ... 延迟表达式的求值,直到需要它的值

采取#2:第二种方式gather表现得懒惰

一些消耗值序列的构造总是懒惰地消耗它们。如果他们消耗的序列是 a gather,他们就会懒惰地要求它的值。在这种情况下,gather有义务评估其序列直到达到 a take,然后屈服直到要求下一个值。

采取#3:一种方式gather表现得热切

一些消耗值序列的构造总是急切地消耗它们。如果他们使用的序列是 a gather,他们会急切地要求它的值。在这种情况下,gather义务和其中的任何懒惰都是没有意义的。

采取#4:第二种方式gather表现得热切

一些消耗值序列的构造基于序列对其调用的回答,或者懒惰急切地要求它们.is-lazy;如果它返回True,那么它的值被懒惰地要求,否则他们急切地被要求。

这里有一个关键的转折:在构造.is-lazy上调用时gather,它返回False

采取#5:您的示例中发生了什么

say .is-lazy
for (gather { take 42 }),                 # False
    (gather { loop { take 42 } });        # False
Run Code Online (Sandbox Code Playgroud)

在您的@f1 = gather ...情况下,@f1被分配了一个序列,表示它不是懒惰的。即使它包含无限循环也是如此。@带有标记的变量将其作为急切分配序列的提示——并且代码挂起。


前缀lazy创建了一个新的Seq,它从它右边的表达式中懒惰地绘制。True如果.is-lazy被调用它也会返回:

say .is-lazy
for (lazy gather { take 42 }),            # True
    (lazy gather { loop { take 42 } });   # True
Run Code Online (Sandbox Code Playgroud)

如果为@sigil'd 变量分配了一个值,该值True在调用 时返回.is-lazy,则赋值和变量都是惰性的。所以代码@f1 = lazy gather ...工作正常。


最后,序列(1...Inf) 知道它是懒惰的,并告诉世界它是懒惰的,不需要前缀lazy

say .is-lazy with (1 ... Inf)             # True
Run Code Online (Sandbox Code Playgroud)

因此,分配也可以正常工作,无论是否有lazy.


总而言之,@如果Seq分配给一个带有符号的变量表示它是惰性的,则它会懒惰地获取元素,否则就会急切地获取元素。


您没有问过这个问题,但另一种情况是将 a 分配或绑定Seq$sigil'd 变量,或 sigil free 标识符。

作为具有@sigil'd变量,调用.is-lazy$sigil'd变量或印记自由标识符将返回TrueFalse在与分配的/约束一致Seq

但是,无论.is-lazy返回True还是FalseSeq仍然会被懒惰地迭代。

  • FWIW,我发现 `say .is-lazy with 1..Inf` 读起来更清晰,因为它不会让你认为它可能会在范围内迭代, (2认同)
  • @ElizabethMattijsen 我用一种我认为有意义的方式修复了它(`say (1..Inf).is-lazy;`,所以没有`for`)。但在编辑之后,您的评论得到了支持。唔。所以现在它已修复为与您所写的完全相同。语言和清晰的编码是一件非常微妙的事情!:) (2认同)