class Enumerator::Lazy

Enumerator::Lazy ist ein spezieller Typ von Enumerator, der das Erstellen von Operationsketten ermöglicht, ohne diese sofort auszuwerten, und Werte nach Bedarf auswertet. Dazu überschreibt er die meisten Methoden von Enumerable so, dass sie lediglich einen weiteren Lazy Enumerator erstellen.

Enumerator::Lazy kann aus jedem Enumerable mit der Methode Enumerable#lazy erstellt werden.

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>

Die eigentliche Enumeration erfolgt, wenn eine nicht überschriebene Methode von Enumerable aufgerufen wird, wie z.B. Enumerable#first oder Enumerable#to_a (letzteres ist als force für semantischeren Code als Alias definiert).

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

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

Beachten Sie, dass die meisten Enumerable-Methoden, die mit oder ohne Block aufgerufen werden können, bei Enumerator::Lazy immer einen Block erfordern.

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

Diese Klasse ermöglicht idiomatische Berechnungen auf langen oder unendlichen Sequenzen sowie das Verketten von Berechnungen, ohne Zwischenarrays zu erstellen.

Beispiel für die Arbeit mit einer langsam berechneten Sequenz

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)

Das Beenden einer Kette mit ".eager" erzeugt einen nicht-faulen Enumerator, der sich zum Zurückgeben oder Übergeben an eine andere Methode eignet, die einen normalen Enumerator erwartet.

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)