Well, enum_if is useful in at least two cases (as I wrote in
the analysis). It could be useful as a replacement for
Enumerable#select that may be more efficient in some cases,
(when dealing with a large amount of data).
For example:
large_dataset.enum_if { |d| sometest(d) }.collect do |d|
<data manipulation>
end
It could also be useful to have a specialized enumerator
that will reflect any changes made to the original object:
You first enum_if example, is a bit confusing:
(0..4).enum_if { |i| i[0] == 0 }.to_a
=> [0, 2, 4]
It took me a while until I recognized that i[0] means the value of the
lowest bit. Just for clearness, could you write either a comment, or
use (i & 0b1) instead? Or (i % 2) == 0.
data = ["a", 6, 9, "foo", -19, "fuga", -19, "bach"]
ints = data.enum_if { |i| i.is_a? Numeric }
ints.to_a
=> [6, 9, -19, -19]
data += ["Bear", 20, 3]
ints.to_a
=> [6, 9, -19, -19, 20, 3]
(I have added these examples to the RCR)
Aha, then it's something like a "lazy" enumerable, right?
Have a look at my code I wrote some weeks ago:
require 'generator'
module Enumerable
def select_lazy(&block)
Generator.new {|c| self.each { |elem| c.yield(elem) if block.call(elem) } }
end
def collect_lazy(&block)
Generator.new {|c| self.each { |elem| c.yield(block.call(elem)) } }
end
alias map_lazy collect_lazy
def lazy
ChainingGenerator.new(self)
end
def no_lazy
to_a
end
end
class ChainingGenerator < Generator
def select(&block)
self.class.new {|c| self.each { |elem| c.yield(elem) if block.call(elem) } }
end
def collect(&block)
self.class.new {|c| self.each { |elem| c.yield(block.call(elem)) } }
end
alias map collect
# TODO: implement others
end
[1,2,3,4].lazy.map{|i| i + 1}.to_a # => [2,3,4,5]
["a", 6, 9, "foo", -19, "fuga", -19, "bach"].lazy.select{|i| i.is_a? Numeric}.to_a
That's a more general form, as after the "lazy", all Enumerable
operations do not create intermediate arrays. Of course, it's very slow
compared to the non-lazy methods (Generator uses continuations).
How performant is enum_if?
Could I write for example:
[1,2,3].enum_if{ cond }.map {|i| i + 1}
Regards,
Michael