From: Martin DeMello Date: 2005-10-27T02:52:03+09:00 Subject: Re: Enumerable#build Robert Klemme wrote: > Martin DeMello wrote: > > class Enumerable > > def build(seed) > > each {|i| > > seed.add(yield i) > > } > > end > > end > > > > and > > > > Array#build == Array#<< > > Hash#build(k,v) == Hash#[] > > etc > > > > So you could say [1,2,3].build({}) {|i| [i, 1]} for the question that > > prompted the to_hash thread, but also, in general, you can collect > > into non-array structures (binary trees, e.g.) as long as they > > implement a suitable #add method. > > > > (Of course, you can do ary.inject(seed) {|i| seed.add(f(i)); seed} > > right now but a #build method makes the common case pretty.) > > Looks good. But, > > - Enumerable is a module... :-) > > - You should add seed as return value. Oops - yes, I was enthused about the idea and got sloppy with the actual code. > - Your first example won't work as Hash#add is nonexistent No, that was my point - add a #add method (chosen not to conflict with anything currently in the core) to any class that you wish to act as a build "client". > An alternative approach would be to reverse the logic and have > Enumerable#populate: > > module Enumerable > def populate(*enum) > enum = enum.first if enum.size == 1 > enum.each {|*a| add(yield(*a))} > self > end > end > > class Hash > def populate(*enum) > enum = enum.first if enum.size == 1 > enum.each {|*a| send(:[]=, *yield(*a))} > self > end > end > > >> h={}.populate(1,2,3) {|x| [x, "val #{x}"]} > => {1=>"val 1", 2=>"val 2", 3=>"val 3"} > > What do you think? I'd make one change to your way - keep Addable separate from Enumerable. Once we've done that, it's simple to add both #populate to Addable (requires an Enumerable argument) and #build to Enumerable (regretting, once again, that #collect is already a synonym of #map). One nice thing is that classes implementing both Enumerable and Addable could then provide a "structural map" - e.g. class Hash def smap build({}) {|k,v| [k, yield(v)]} end end {:x=>2, :y=>4}.smap {|i| i*i} #=> {:x => 4, :y => 16} Or even something more complex like class Tree def each ... yield [element, path] end def add args element, path = args ... end def smap build(Tree.new) {|e, path| [(yield e), path } # or # Tree.new.populate(self) {|e, path| [(yield e), path]} end end martin