In the Ruby Array Class documentation, I often find:
If no block is given, an enumerator is returned instead.
Why would I not pass a block to #map
? What would be the use of my doing just:
[1,2,3,4].map
instead of doing:
[1,2,3,4].map{ |e| e * 10 } # => [10, 20, 30, 40]
Can someone show me a very practical example of using this enumerator?
The main distinction between an Enumerator
and most† other data structures in the Ruby core library (Array
, Hash
) and standard library (Set
, SortedSet
) is that an Enumerator
can be infinite. You cannot have an Array
of all even numbers or a stream of zeroes or all prime numbers, but you can definitely have such an Enumerator
:
evens = Enumerator.new do |y|
i = -2
y << i += 2 while true
end
evens.take(10)
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
zeroes = [0].cycle
zeroes.take(10)
# => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
So, what can you do with such an Enumerator
? Well, three things, basically.
Enumerator
mixes in Enumerable
. Therefore, you can use all Enumerable
methods such as map
, inject
, all?
, any?
, none?
, select
, reject
and so forth. Just be aware that an Enumerator
may be infinite whereas map
returns an Array
, so trying to map
an infinite Enumerator
may create an infinitely large Array
and take an infinite amount of time.
There are wrapping methods which somehow "enrich" an Enumerator
and return a new Enumerator
. For example, Enumerator#with_index
adds a "loop counter" to the block and Enumerator#with_object
adds a memo object.
You can use an Enumerator
just like you would use it in other languages for external iteration by using the Enumerator#next
method which will give you either the next value (and move the Enumerator
forward) or raise
a StopIteration
exception if the Enumerator
is finite and you have reached the end.
† Eg., an infinite range: (1..1.0/0)