Nik*_*nes 11 iterable lazy-evaluation lazy-sequences raku
我想获得 Raku 中惰性但有限的 Seq 的最后一个元素,例如:
my $s = lazy gather for ^10 { take $_ };
Run Code Online (Sandbox Code Playgroud)
以下内容不起作用:
say $s[* - 1];
say $s.tail;
Run Code Online (Sandbox Code Playgroud)
这些可以工作,但看起来不太惯用:
say (for $s<> { $_ }).tail;
say (for $s<> { $_ })[* - 1];
Run Code Online (Sandbox Code Playgroud)
在保持原始 Seq 惰性的同时,最惯用的方法是什么?
cod*_*ons 17
您所问的问题(“获取惰性但有限的 Seq \xe2\x80\xa6 的最后一个元素,同时保持原始 Seq 惰性”)是不可能的。我并不是说Raku \xe2\x80\x93不可能,我的意思是,原则上,任何语言都不可能像 Raku 那样定义“惰性”,例如,is-lazy方法。
如果特别的话,当 Seq 在 Raku 中是惰性的时,“意味着[Seq的]值是按需计算并存储以供以后使用。” 此外,惰性可迭代的定义特征之一是它在保持惰性 \xe2\x80\x93 的同时无法知道自己的长度,这就是调用.elems惰性可迭代会引发错误的原因:
my $s = lazy gather for ^10 { take $_ };\nsay $s.is-lazy; # OUTPUT: \xc2\xabTrue\xc2\xbb\n$s.elems; # THROWS: \xc2\xabCannot .elems a lazy list onto a Seq\xc2\xbb\nRun Code Online (Sandbox Code Playgroud)\n现在,此时,您可能会合理地想“好吧,也许Raku不知道有多长$s,但我可以看出它正好有 10 个元素”。你没有错 \xe2\x80\x93 与该代码,$s确实保证有 10 个元素。这意味着,如果您想获取 的第十个(最后一个)元素$s,可以使用 来实现$s[9]。像这样访问$s第十个元素不会改变 的事实$s.is-lazy。
但是,重要的是,您只能这样做,因为您了解有关 的一些“额外”信息$s,并且这些额外信息消除了您可能希望列表在实践中偷懒的大部分原因。
为了明白我的意思,考虑一个非常相似的Seq
my $s2 = lazy gather for ^10 { last if rand > .95; take $_ };\nsay $s2.is-lazy; # OUTPUT: \xc2\xabTrue\xc2\xbb\nRun Code Online (Sandbox Code Playgroud)\n现在,$s2可能有 10 个元素,但可能不是 \xe2\x80\x93 ,唯一知道的方法是迭代它并找出答案。反过来,这意味着 $s2[9]不会像以前那样跳转到第十个元素$s[9];$s2它会像您需要的那样进行迭代。并且,结果,如果你运行$s2[9],那么$s2将不再偷懒(即,$s2.is-lazy将返回False)。
实际上,这就是您在问题代码中所做的事情:
\nmy $s = lazy gather for ^10 { take $_ };\nsay $s.is-lazy; # OUTPUT: \xc2\xabTrue\xc2\xbb\nsay (for $s<> { $_ }).tail; # OUTPUT: \xc2\xab9\xc2\xbb\nsay $s.is-lazy; # OUTPUT: \xc2\xabFalse\xc2\xbb\nRun Code Online (Sandbox Code Playgroud)\n因为 Raku 永远无法知道它已经达到了tail惰性 Seq 的 ,所以它能告诉你的唯一方法就是.tail完全迭代$s。这必然意味着$s不再懒惰。
值得一提的是两个相邻的主题,它们实际上并不相关,但足够接近,足以让一些人绊倒。
\n首先,我没有说过关于不知道其长度的惰性可迭代对象会阻止某些非惰性可迭代对象知道其长度。事实上,相当数量的 Raku 类型同时扮演迭代器角色和PredictiveIterator角色 \xe2\x80\x93 , a 的要点PredictiveIterator是它确实知道它可以生成多少个元素,而无需生成/迭代它们。但也PredictiveIterators 不能偷懒。
第二个可能令人困惑的主题与第一个主题密切相关:虽然 noPredictiveIterator不能是懒惰的(也就是说,没有一个会具有.is-lazy返回 的方法True),但某些PredictiveIterators 的行为与懒惰 \xe2\x80\x93 非常相似,并且,在事实上,甚至可以通俗地称为“懒惰”。
我无法很好地解释这种区别,因为老实说,我自己也没有完全理解它。但我可以给你一个例子:.lines方法在IO::Handle. 当然,读取大文件的行的行为很像处理惰性可迭代对象。最明显的是,您可以处理每一行,而无需将整个文件存储在内存中。文档甚至说使用该.lines方法“延迟读取行”。
另一方面:
\nmy $l = \'some-file-with-100_000-lines.txt\'.IO.lines;\nsay $l.is-lazy; # OUTPUT: \xc2\xabFalse\xc2\xbb\nsay $l.iterator ~~ PredictiveIterator; # OUTPUT: \xc2\xabTrue\xc2\xbb\nsay $l.elems; # OUTPUT: \xc2\xab100000\xc2\xbb\nRun Code Online (Sandbox Code Playgroud)\n所以我不太确定说“是一个惰性可迭代”是否公平$l,但如果是的话,它的“惰性”方式与以前不同$s。
我意识到这很多,但我希望它有帮助。如果您有一个更具体的惰性用例(我敢打赌它不是收集从零到九的数字!),我很乐意更具体地解决这个问题。如果其他人可以填写一些细节 和.lines其他懒惰不懒惰PredictiveIterators,我真的很感激!
rai*_*iph 10
lazyRaku 中的惰性序列被设计为可以正常工作。你不需要通过添加显式的方式来强调他们是懒惰的lazy.
如果您添加显式 lazy,Raku 会将其解释为阻止操作的请求,例如.tail它们几乎肯定会立即使惰性变得毫无意义,并且,如果在无限序列上调用,甚至只是一个足够大的序列,则会挂起或 OOM 程序。
因此,要么删除lazy,要么不调用类似的操作.tail,否则将被阻止。
正如 @ugexe 所指出的,惯用的解决方案是删除lazy.
引用我对 SO About Laziness的回答的回答:
\n\n\n如果询问 a
\ngather是否是惰性的,它会返回False。
Aiui,类似以下内容适用:
\n一些惰性序列生成器实际上或实际上是无限的。如果是这样,请致电.tail对它们调用 etc 将挂起调用程序。相反,其他惰性序列在一次性消耗所有值时表现良好。Raku应该如何区分这两种情况?
2015 年做出了一项决定,让产生价值的数据类型通过对调用的响应来强调或弱化其惰性.is-lazy。
返回True信号表明序列不仅是惰性的,而且还希望通过使用调用 的代码来让人们知道.is-lazy它是惰性的。(不是那么多最终用户代码,而是内置的消费功能,例如@处理分配的符号变量,试图确定是否急切地分配。)内置的消费功能将 aTrue作为信号,它们应该阻止诸如.tail. 如果开发人员知道这过于保守,他们可以添加eager(或删除不需要的lazy)。
相反,数据类型,甚至特定的对象实例,可能会返回False以表明它不希望被视为惰性的。这可能是因为特定数据类型或实例的实际行为是急切的,但也可能是因为它在技术上是懒惰的,但不希望消费者阻止操作,例如因为它知道它们不会有害,或者至少更愿意将其作为默认假设。如果开发人员更了解(因为,比如说,它挂起程序),或者至少不想阻止可能有问题的操作,他们可以添加(或删除不需要的)。.taillazyeager
我认为这种方法效果很好,但它的文档和错误消息中提到的“懒惰”可能没有跟上 2015 年的转变。所以:
\n如果您对某些有关懒惰的文档感到困惑,请搜索其中包含“lazy”或“laziness”的文档问题,并向现有问题添加评论,或提交新的文档问题(可能链接到此答案) )。
\n如果您对提到懒惰的 Rakudo 错误消息感到困惑,请搜索其中包含“lazy”并标记[LTA]为“Less Than Awesome”的Rakudo 问题,并添加评论,或提交新的 Rakudo 问题(带有[LTA]标签,也许还有指向此答案的链接)。
\n\n文档...说 \xe2\x80\x9c如果你想强制延迟计算,请使用
\nlazy子例程或方法。绑定到标量或无符号容器也会强制惰性。\xe2\x80\x9d
是的。艾伊伊这是正确的。
\n\n\n[这]听起来像是暗示 \xe2\x80\x9c与 \xe2\x80\x9d
\nmy $x := lazy gather { ... }相同my $x := gather { ... }。
不。
\n显式的lazy语句前缀或方法强调了惰性,Raku 解释说这意味着它应该阻止操作,就像.tail它们挂起程序一样。
相反,绑定到变量既不会改变惰性的强调,也不会改变惰性,只是转发绑定的生产者数据类型/实例选择通过 传达的任何内容.is-lazy。
\n\n不仅与其他方面有关,
\ngather而且在其他方面也如此
是的。这是关于以下结果的.is-lazy:
my $x = (1, { .say; $_ + 1 } ... 1000);\nmy $y = lazy (1, { .say; $_ + 1 } ... 1000);\nRun Code Online (Sandbox Code Playgroud)\n\n\n两者的行为都很懒惰……但
\n$x.tail有可能,但$y.tail不可能。
是的。
\n显式 语句前缀lazy或方法强制答案.is-lazy为True。这向关心懒惰危险的消费者发出信号,表明他们应该变得谨慎(例如拒绝.tail等)。
(相反,eager可以使用语句前缀或方法来强制答案为.is-lazy,False使胆怯的消费者接受.tail等调用。)
\n\n我由此认为,Raku 中有两种惰性,其中一种必须小心,看看哪一种用在哪里。
\n
我称之为消费指导有两种:
\nDon\t-tail-me如果一个对象True从.is-lazy调用中返回,那么它会被视为可能是无限的。因此类似的操作.tail被阻止。
You-can-tail-me如果一个对象False从调用中返回,则接受.is-lazy类似的操作。.tail
并不需要特别注意这两种类型中的哪一种,但如果想要调用类似 的操作tail,那么可能需要通过插入eager或删除 a来启用它lazy,并且造成的后果必须承担责任:
如果程序由于使用DIHWIDT.tail而挂起。
如果您突然消耗了所有惰性序列并且尚未缓存它,那么,也许您应该缓存它。
\nETC。
\n我想说的是,错误消息和/或文档可能很需要改进。
\n