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