Feature #708
closedLazy Enumerator#select, Enumerator#map etc.
Description
There are a number of methods in Enumerable
that build an Array
of results from the entire collection: map
, select
, take
, etc.
I propose that the Enumerator
class have its own implementations of these methods, which return another Enumerator
. Enumerators can then be chained:
seq.to_enum.map { ... }.select { ... }.take(...).each { |x| puts x }
This runs horizontally, that is, each element is processed left to right. No intermediate arrays are created, and it works happily with sequences of arbitrary length.
There are precendents for SomeClass#select
behaving differently to Enumerable#select
. For example, Hash#select
returns a Hash
. So I believe it would be reasonable for Enumerator
to return another Enumerator
.
You can then choose between array-building or lazy evaluation, depending on whether there is an Enumerator in the chain. Of course, the last Enumerator has to be turned into something useful, e.g. by calling to_a
or each { ... }
.
Normal
res = (1..1_000_000).map { |x| x * 2 }.take(100)
Lazy
res = (1..1_000_000).to_enum.map { |x| x * 2 }.take(100).to_a
I have attached a simple implementation of this for select
, map
, take
and a new method skip
. There are further methods like take_while
, zip
and so on which would also need to be implemented.
Files