ruby - 如何在 Enumerator::Lazy 方法中停止迭代?

标签 ruby lazy-evaluation enumerator

我正在尝试为 Ruby 2 的 Enumerator::Lazy 类实现一个 take_until 方法。它的工作方式应该与 take_while 类似,但在 yielded block 返回 true 时停止迭代。结果应包括生成的 block 匹配的项。

我的问题是如何发出迭代结束的信号?使用常规枚举器时,您可以在 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 方法,它做的事情与我想要的类似。它不使用 block 作为条件,而是使用数字,但一旦达到该数字,它就会跳出迭代,而不是再评估一个项目。继续上面的例子:

enum.lazy.take(5).force

这不会到达第 6 个项目,因此不会阻塞。问题是标准库中的版本是用 C 实现的,我似乎无法弄清楚如何在纯 Ruby 中实现它。该方法的 ruby 实现将是一个可接受的答案。

提前致谢!

最佳答案

这是一个老问题,但无论如何:正如你所说,你真正需要的是一个Lazy#take_until,当然Lazy#take_while将需要获取下一个项目来决定是否打破或不。我一直无法使用 Lazy#new { ... } 实现 Lazy#take_until,显然没有中断机制。这是一个可能的解决方法:

class Enumerator::Lazy  
  def take_until
    Enumerator.new do |yielder|
      each do |value|
        yielder << value
        break if yield(value)
      end
    end.lazy
  end
end

关于ruby - 如何在 Enumerator::Lazy 方法中停止迭代?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20751856/

相关文章:

Ruby 对象打印格式

scala - 选择 def 而不是 val 的优缺点

clojure - Clojure 的 memoize 是否会强制对其参数进行评估?

python - *参数调用是懒惰的吗?

c# - 接口(interface)实现和返回类型

ruby - Ruby 中 Enumerator 类的用途是什么

ruby - 为什么动态创建类时没有触发const_missing

ruby-on-rails - 单个延迟作业 worker 可以在正在进行的作业结束之前开始下一个作业吗?

ruby-on-rails - Carrierwave gem 在 Redmine v 2.x 插件中不起作用

c# - HashSet 枚举器有什么作用?