From: Kristof Bastiaensen Date: 2004-06-18T22:13:25+09:00 Subject: Re: [RCR] More enumerator functionality On Fri, 18 Jun 2004 21:28:45 +0900, Michael Neumann wrote: > 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. > > You have a good point. I changed it. >> 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? Yes, that's right. The given block will only be executed at the time of yielding the corresponding value. > 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). > > That's interesting. If I am correct, your collect_lazy and lazy.collect behaves the same as enum_for with a block. Your select_lazy and lazy.select the same as enum_if. > How performant is enum_if? It should be quite fast, since all it does is pass each yield through the block. Even more so, since Nobu Nokada was kind enough to provide an implementation in c. :-) > Could I write for example: > > [1,2,3].enum_if{ cond }.map {|i| i + 1} > > Yes, exactly. That was also the kind of thing I had in mind. Cheers, Kristof