What is Lazy Enumerator

Ruby 2.0 lazy enumerator allows you to iterate over infinite values and take the values you want for your calculation.This code would result in an infinite loop.

range = 1..Float::Infinity
range.collect { |x| x*x  }.first(10)

The code results in an infinite loop because the Enumerable#collect is eager.It means that first collect processes all the values from the first array and then save it in the second array.Since we have an infinite range here first method would never get a chance to execute.

With ruby 2.0 you can use lazy method and take only the values you want like

range = 1..Float::Infinity
range.lazy.collect { |x| x*x  }.first(10)
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Currently it supports select, map, reject, grep and flat_map methods.

** Why We need lazy enumerator **

Suppose we have code like

data.map(&:split).map(&:reverse)

In this example while mapping data array unnecessary intermediate arrays are generated.It is okay when we have small sets of values.But when we have to iterate over a large excel file this becomes a performance issue.In this case you can use the lazy method to avoid unnecessary memory consumption.By using Lazy map/collect method it evaluates the whole chain when the actual result needed and iterate over once with the original data.

** How Does it work **

Lazy evaluation in ruby implemented using an object Enumerator#Lazy.It contains Enumerable methods like map, select etc.It calls each to obtain data and yield data to enumeration.You can chain them to produce single enumeration.
In our example:

range = 1..Float::Infinity
range.lazy.collect { |x| x*x  }.first(10)
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

When we call to lazy on enumerator it produces an Enumerator#Lazy oject.Then when we call collect on this Enumerator#Lazy object, the Enumerator#Lazy method returns a second object.

enter image description here

Each value from the enumerator is passed to my block (x*x in this case) and then result is passed down to the enumeration chain.Each value is passed one at a time along the chain of Enumerator#Lazy objects.So if we have chained Enumerator#lazy methods ( like map, select) each value would be passed along the chain.

So in our example Enumerable#first starts the iteration by calling each on Enumerator#lazy objects and it also ends when it has enough(10) values.You can dig dipper in the details of how exactly the code works by referring to link below:

References:

ruby-2-0-enumerablelazy

ruby-2-0-works-hard-so-you-can-be-lazy