类 Enumerator::Lazy

Enumerator::Lazy 是一种特殊的 Enumerator 类型,它允许构造操作链而不立即计算它们,并且根据需要计算值。为此,它重新定义了大多数 Enumerable 方法,使它们只构造另一个惰性枚举器。

Enumerator::Lazy 可以从任何 Enumerable 中使用 Enumerable#lazy 方法构造。

lazy = (1..Float::INFINITY).lazy.select(&:odd?).drop(10).take_while { |i| i < 30 }
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:select>:drop(10)>:take_while>

当调用任何未重新定义的 Enumerable 方法时,会执行实际的枚举,例如 Enumerable#firstEnumerable#to_a (后者别名为 force 以获得更语义化的代码)

lazy.first(2)
#=> [21, 23]

lazy.force
#=> [21, 23, 25, 27, 29]

请注意,大多数可以带或不带块调用的 Enumerable 方法在 Enumerator::Lazy 上总是需要一个块

[1, 2, 3].map       #=> #<Enumerator: [1, 2, 3]:map>
[1, 2, 3].lazy.map  # ArgumentError: tried to call lazy map without a block

此类允许对长序列或无限序列进行惯用计算,以及在不构造中间数组的情况下链接计算。

使用缓慢计算序列的示例

require 'open-uri'

# This will fetch all URLs before selecting
# necessary data
URLS.map { |u| JSON.parse(URI.open(u).read) }
  .select { |data| data.key?('stats') }
  .first(5)

# This will fetch URLs one-by-one, only till
# there is enough data to satisfy the condition
URLS.lazy.map { |u| JSON.parse(URI.open(u).read) }
  .select { |data| data.key?('stats') }
  .first(5)

以 “.eager” 结尾的链生成一个非惰性枚举器,该枚举器适合返回或传递给期望普通枚举器的另一个方法。

def active_items
  groups
    .lazy
    .flat_map(&:items)
    .reject(&:disabled)
    .eager
end

# This works lazily; if a checked item is found, it stops
# iteration and does not look into remaining groups.
first_checked = active_items.find(&:checked)

# This returns an array of items like a normal enumerator does.
all_checked = active_items.select(&:checked)