From: Intransition Date: 2010-04-04T08:47:09+09:00 Subject: Re: combined ranges... On Apr 3, 11:47 am, Colin Bartlett wrote: > On Sat, Apr 3, 2010 at 2:30 PM, Intransition wrote: > > Here, I worked on it I came up with: > > Is this the Array#recursively "TODO: Can this be generalized in Enumerable?" :)http://facets.rubyforge.org/apidoc/api/core/classes/Array.html#M000055 Thanks you for pointing this one out. I am working to make these methods more uniform/consistent across all of Facets. There is also similar methods for Pathname and Dir (e.g. Dir#recurse). > Oddly enough, I'd also started wondering about a fully recursive version! > Borrowing ideas from Facets (Array#recursively, Enumerable#traverse) > I came up with the code at the end of this post. It's not as general > as either your code or Robert Klemme's (sort of "orthogonal" to yours) > but it was intended to cope with an element of the array > having an each method but not mixing in Enumerable > > > I am going to add this to Facets, but I have to decide for sure on the > > name. Which is better?   #visit   #traverse   #recurse   other ? > > I know they are long names, but because it's a recursive each, why not >   #each_recursively (Facets already uses recursively) or #each_recursive > (I prefer the latter - shorter!) and maybe have visit as a short alias? > Or maybe use Robert Klemme's #each_flat, although since this will > apply a recursive each to Ranges (or Files, or anything else using each > which mixes in Enumerable) I'm not sure that flatten (or a derivative) > is fully appropriate? Yes, I was thinking of #recursive_each as well, since that name is also along the lines of #reverse_each. On the other hand #recurse is nice and short and to the point. > I also like Robert Klemme's idea of having a level parameter, > so if Robert is agreeable - and from his Ruby-talk posts he seems to be > a very agreeable person! - maybe include levels, maybe renamed depth? > (I've thought of using level in a more general Find module, and > eventually decided depth was a better word for the concept. I can't > remember why, but I'm sure there must have been very good reasons!) > And maybe cater for "unlimited" depth using depth/levels = nil ? So: >      if levels > 1 && Enumerable === x >        x.each_flat(levels - 1, &b) > would become >      if (! levels || levels > 1) && Enumerable === x >        x.each_flat( levels && levels - 1, &b) > > A question: should the depth/levels test be levels > 1 or > 0? > Using [ 2..2 ] and "levels > 1": >   levels param: 0 #=> 2..2;   1 #=> 2..2;   2 #=> 2; > Using [ 2..2 ] and "levels > 0": >   levels param: 0 #=> 2..2;   1 #=> 2;      2 #=> 2; > It seems (maybe?) more logical to have depth/levels arguments: >   0 means don't do any recursive expansion of each; >   1 means do some recursive expansion of each; Yes, I agree, a depth parameter could be useful as well. > ### Intended to cope with an element of an array (or whatever) > ### having an each method but not mixing in Enumerable. > module Enumerable >   def visits(&block) >     each { |item| Enumerable.visits?(item, &block) } >   end >   def Enumerable.visits?(obj, &block) >     if obj.respond_to?( :each ) then >       obj.each { |item| Enumerable.visits?(item, &block) } >     else >       yield obj >     end >   end > end Interesting, you could take this a step further. class Object def visits(&block) if respond_to?(:each) each{ |item| item.visits(&block) } else yield(self) end end end Then every object would be "visitable". However, using respond_to? can be dangerous. Consider if I (foolishly) did: class Object def each yield(self) end end The more OOP approach would be: class Object def visits yield(self) end end module Enumerable def visits(&block) each{ |item| item.visits(&block) } end end Then you can override #visits in any object as desired.