From: Michael Neumann Date: 2004-06-18T21:28:45+09:00 Subject: Re: [RCR] More enumerator functionality On Fri, Jun 18, 2004 at 08:58:33PM +0900, Kristof Bastiaensen wrote: > On Fri, 18 Jun 2004 01:54:19 +0900, Michael Neumann wrote: > > > On Fri, Jun 18, 2004 at 12:03:44AM +0900, Kristof Bastiaensen wrote: > >> Hi everybody, > >> > >> I have finally posted my RCR for more enumerator > >> functionality. > >> > >> Find more about it here: > >> http://rcrchive.net/rcr/RCR/RCR262 > > > > Can you give some more examples, where and how enum_if could be useful? > > > > Regards, > > > > Michael > > 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| > > 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