From: Robert Klemme Date: 2010-04-12T05:55:12+09:00 Subject: Re: Is it possible, a fully general Enumerable#recursive ? On 04/09/2010 08:53 PM, Intransition wrote: > > On Apr 9, 2:03 pm, Robert Dober wrote: >> On Thu, Apr 8, 2010 at 5:51 AM, David Masover wrote: >>> On Wednesday 07 April 2010 04:49:15 pm Intransition wrote: >> >> >>>> This works for #each and #map but not #sort. >>> #sort isn't a method of Enumerable, it's a method of Array. >> ruby-1.9.1-p378 > Enumerable.instance_methods.grep /sort/ >> => [:sort, :sort_by] >> However you will need to define #<=> on the return value of recursive. Why that? The whole point would be to sort all elements that would be recursively returned. The #recursive return value would never be seen then. > Yep. That's trick b/c comparing and array or other enumerable to a non > enumerable raises an error. So sorting with this is probably out of > the question. On a side note, I am not so sure that raising an error > is best. Why not just assume that too non-comparable things are equal? See above. Here are my two solutions with plain old Enumerator approach. I do not see the benefit of a Functor here. 1. simple module Enumerable def recursive(&b) if b each do |e| if Enumerable === e e.recursive(&b) else b[e] end end self else Enumerator.new(self, :recursive) end end end 2. catch loops module Enumerable def recursive(items = {}, &b) if b each do |e| items.fetch e.object_id do |oid| items[oid] = e if Enumerable === e e.recursive(items, &b) else b[e] end end end self else Enumerator.new(self, :recursive) end end end This one suffers the fragility that callers providing something else than an empty Hash can break it. That could be fixed with a bit more effort by using a thread local. Just using the simple approach is probably better since there is no notification of loops anyway so the using code has zero chance to handle the case of loops which might be important to know. Another note: your original code suffered from the inability to mix different Enumerables because you use "self.class" for the type check. I changed that by only testing for Enumerable. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/