ruby - 在 Ruby 中概念化枚举器和惰性枚举器

标签 ruby

我需要一些帮助来概念化 Ruby 中的 Lazy Enumerator 对象。

我认为 Enumerator 对象是集合的“有状态”版本。该对象了解哪些元素是集合的一部分,以及应该产生的“下一个”元素是什么。它是一个知道它的元素是 1、2 和 3 的对象,它已经产生 1 和 2,因此如果被要求它会产生 3。

假设 Enumerator 的概念化是正确的,我很难理解 Lazy Enumerator 的工作原理。惰性枚举器是在“常规”枚举器的基础上构建的,但不应该事先计算其集合。例如,来自 The Ruby Way:

enum = (1..Float::INFINITY).each
lazy = enum.lazy
odds = lazy.select(&:odd)

如果惰性枚举器是从惰性枚举器构建的,那么惰性枚举器是如何惰性的,因为我正在做枚举器部分,这可能不是惰性的,首先?

最佳答案

Enumerator#lazy 允许您有效地创建一系列可枚举操作,这些操作应用于从可枚举迭代而来的每个值。相比之下,普通枚举器对可枚举中的所有值执行每个操作,然后将结果沿着链向下传递到链中的下一个操作。您可以将惰性枚举器视为“深度优先”操作,将普通枚举器视为“广度优先”操作。

普通枚举器返回枚举的结果:

> (1..10).select(&:odd?)
 => [1, 3, 5, 7, 9]

如果您要链接这些操作,您可以对有限的值列表执行一些操作列表:

> (1..10).select(&:odd?)
 => [1, 3, 5, 7, 9]

> (1..10).select(&:odd?).map {|v| v * 2 }
 => [2, 6, 10, 14, 18]

在将值列表传递到链下以进行下一个操作之前,链中的每个操作都应用于可枚举中的所有值。

相比之下,Enumerable#lazy 从每个操作中返回惰性枚举器(“与值相关的事情”):

> (1..10).lazy.select(&:odd?)
 => #<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:select>
> (1..10).lazy.select(&:odd?).map {|v| v * 2 }
 => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:select>:map>

如您所见,它不会在链中的每次调用中处理整个值列表。相反,当您从可枚举中合并一个值时(例如,使用#next),然后从底层可枚举中获取下一个值,然后通过每个可枚举操作传递,最后返回:

> (1..10).lazy.select(&:odd?).map {|v| v * 2 }.next
 => 2

如果您要在无限列表上尝试这些相同的操作,那么非惰性枚举器将永远停止,因为它会尝试对无限枚举进行广度优先传递,这显然永远不会终止!

关于ruby - 在 Ruby 中概念化枚举器和惰性枚举器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30416665/

相关文章:

ruby - Sinatra 中所有 POST 请求的前置过滤器?

ruby-on-rails - 在 has_many :through association, 用 lambda 替换条件

ruby-on-rails - Ruby 从多维数组中提取数组

ruby-on-rails - 在 Rails 中保存到数据库时,时间属性会发生更改

ruby 检测方法

ruby-on-rails - Rails 控制台为包含值的列返回 nil

ruby-on-rails - 如何让我的导航栏出现在我的 Rails 应用程序的每个页面上?

ruby - Savon 2 如何将属性添加到 message_tag

Ruby 添加一个日历月

ruby-on-rails - 嵌套模型形式;如何访问父/根FormBuilder?