【发布时间】:2014-01-12 03:54:33
【问题描述】:
我正在尝试为 Ruby 2 的 Enumerator::Lazy 类实现 take_until 方法。它应该类似于take_while 工作,但是当产生的块返回真时停止迭代。结果应该包括产生的块匹配的项目。
我的问题是如何发出迭代结束的信号?使用常规枚举器时,您可以在 each 方法中引发 StopIteration 错误以指示迭代器的结束。但这似乎不适用于惰性枚举:
class Enumerator::Lazy
def take_until
Lazy.new(self) do |yielder, *values|
yielder << values
raise StopIteration if yield *values
end
end
end
(1..Float::INFINITY).lazy.take_until{ |i| i == 5 }.force
我也试图突破障碍,但没有效果。 The documentation for Enumerator::Lazy 似乎也没有帮助。
为什么使用take_while 不是一个有效的选项。
take_while 的主要问题在于,它的本质是会尝试评估多于您需要的一项。在我的应用程序中,枚举器不会产生数字,而是通过网络获取的消息。试图评估不存在的消息(还没有?)是一种非常不可取的阻止操作。以下人为设计的示例说明了这一点:
enum = Enumerator.new do |y|
5.times do |i|
y << i
end
sleep
end
enum.lazy.take_while{ |i| i < 5 }.force
要接收来自该枚举器的前五个项目,您需要评估第六个结果。这并不像它可能的那么懒惰。在我的用例中,这是不可取的,因为进程会阻塞。
为 Enumerator::Lazy 提供 take 的纯 Ruby 实现
标准库包含一个take 方法,它的功能与我想要的类似。它不使用块作为条件,而是使用数字,但是一旦达到该数字,它就会中断迭代,而不是再评估一个项目。继续上例:
enum.lazy.take(5).force
这不会到达第 6 项,因此不会阻塞。问题是标准库中的版本是用 C 实现的,我似乎无法弄清楚如何在纯 Ruby 中实现它。该方法的 ruby 实现将是一个可以接受的答案。
提前致谢!
【问题讨论】:
-
我不想问,但你不能只使用 take_while 并根据需要修改条件吗?
-
这是一个有效的问题,但我认为答案是:不,我不能。我的用例涉及一个响应流,我特别想在遇到特定条件时对序列进行定界。 Take_while 不会包含匹配项本身,而是将序列返回到第一个“未命中”。希望这是有道理的。
-
所以
take_until将是一个否定的take_while,它会增加一个yield next? -
@steenslag 看起来像。
-
另一个注意事项:如果有人可以提供
take或take_while的 Ruby 实现,我想我可以从中得出我的答案。
标签: ruby lazy-evaluation enumerator