From: Intransition Date: 2010-04-13T00:51:36+09:00 Subject: Re: Is it possible, a fully general Enumerable#recursive ? On Apr 11, 4:55 pm, Robert Klemme wrote: > >> 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. Do you run into the issue of sorting [1,2,3, [:a,:b,:c]] where by an error is raised when it tries, 1 <=> [:a,:b,:c] ? > > 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 Yes, that's the basic definition. It does have issues though. For one, the Enumerator isn't very useful, as it doesn't seem able to handle the recursion very will (compare #with_index and #map, etc.). Another issue is hashes, they won't iterate well here. In Ruby 1.8.7 or less you also get an infinite loop b/c String's are Enumerable, so an exception really needs to be made for them. Other than all that it works ;-) > 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 Nice use of #fetch, btw. > 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. Is it important? recursive graphs seem so rare anyway, and if your doing an recursive iteration wouldn't an infinite loop be expected? Just as if one were iterating over an infinite sequence? > 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. I have two versions actually, one testing for Enumerable and one testing for self.class, I think both are useful. But you are right, to address the original question (of the previous thread on this topic) testing for Enumerable is the one needed..