From: Intransition on 9 Apr 2010 14:49 On Apr 9, 11:01 am, Intransition wrote:> On Apr 7, 5:49 pm, Intransition wrote: > > > For the last couple of days I've been trying to write an Enumerable > > method called #recursive. Rather than create a bunch of methods like > > #recursive_each, #recursive_map, #recursive_sort, etc. I figured that > > it should be possible to create a single #recursive method that > > returned an Enumerator, or barring that a Functor, that would handle > > any enumerable method, e.g. recursive.each, recursive.map, > > recursive.sort, and so on. But I have yet to figure out fully general > > solution. > > Working on this more I currently have the method #visit (see the code > below). It almost works, but it has this one issue that makes no sense > to me, and I wonder what is going on under the hood in Enumerator for > it do this. It has the air of a bug to me --or at least a feature > deficiency. > > Notice: > >   [1, 2, 3, ['a', 'b', 'c'] ].visit{ |x| x.succ } >   => [2, 3, 4, ["b", "c", "d"]] > > But using Enumerator: > >   [1, 2, 3, ['a', 'b', 'c'] ].visit.map{ |x| x.succ } >   => [2, 3, 4, "b", "c", "d"] > > Why is it flattening the result? To just make that much stranger: [1, 2, 3, ['a', 'b', 'c'] ].visit.with_index{ |x,i| i } => [0, 1, 2, [3, 4, 5]] Doesn't flatten, but somehow the index is being carried on to the subarray iterations -- that really fracks with my mind. From: Intransition on 9 Apr 2010 14:53 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. 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? From: Caleb Clausen on 9 Apr 2010 15:02 On 4/9/10, Intransition wrote:>> Notice: >> >> [1, 2, 3, ['a', 'b', 'c'] ].visit{ |x| x.succ } >> => [2, 3, 4, ["b", "c", "d"]] >> >> But using Enumerator: >> >> [1, 2, 3, ['a', 'b', 'c'] ].visit.map{ |x| x.succ } >> => [2, 3, 4, "b", "c", "d"] >> >> Why is it flattening the result? > > To just make that much stranger: > > [1, 2, 3, ['a', 'b', 'c'] ].visit.with_index{ |x,i| i } > => [0, 1, 2, [3, 4, 5]] > > Doesn't flatten, but somehow the index is being carried on to the > subarray iterations -- that really fracks with my mind. This has got to be a bug. It should at least either flatten or not flatten consistently. > 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? Maybe it would help you write a #visit that sorts properly, but in general, I would expect that if I try to sort a collection containing non-comparable objects it's because I made a mistake and put the wrong thing into the collection somehow. So I want to see that error. From: Colin Bartlett on 9 Apr 2010 18:38 On Fri, Apr 9, 2010 at 8:02 PM, Caleb Clausen wrote:> On 4/9/10, Intransition wrote: >>> Notice: >>>   [1, 2, 3, ['a', 'b', 'c'] ].visit{ |x| x.succ } >>>   => [2, 3, 4, ["b", "c", "d"]] >>> >>> But using Enumerator: >>> >>>   [1, 2, 3, ['a', 'b', 'c'] ].visit.map{ |x| x.succ } >>>   => [2, 3, 4, "b", "c", "d"] >>> >>> Why is it flattening the result? >> To just make that much stranger: >>   [1, 2, 3, ['a', 'b', 'c'] ].visit.with_index{ |x,i| i } >>   => [0, 1, 2, [3, 4, 5]] >> Doesn't flatten, but somehow the index is being carried on to the >> subarray iterations -- that really fracks with my mind. > > This has got to be a bug. It should at least either flatten or not > flatten consistently. I'm not sure that it is a bug. http://www.ruby-doc.org/ruby-1.9/classes/Enumerable.html#M002713 enum.map {| obj | block } => array Returns a new array with the results of running block once for every element in enum. http://www.ruby-doc.org/ruby-1.9/classes/Enumerable.src/M002713.html static VALUE enum_collect(VALUE obj) { VALUE ary; RETURN_ENUMERATOR(obj, 0, 0); ary = rb_ary_new(); rb_block_call(obj, id_each, 0, 0, collect_i, ary); return ary; } The following is Intransition's methods with some added info. It looks to me as though two things are happening. 1. Using #map inside #visit combined with using Enumerable#map seems to (within #visit) produce the structure of the original array, but with nil values instead of the original values. (Changing #map inside #visit to #each gets back the original values.) 2. Irrespective of the internal results from #visit, Enumerable#map collects the successive results of the block for Enumerable#map into a new (flat) array. module Enumerable def visit(&block) puts "#=> Enumerable#visit: self=0x#{self.object_id}= #{self.inspect}" if block_given? then result = map do |e| e.visit(&block) end else result = to_enum(:visit) end puts "#=> Enumerable#visit: result=0x#{result.object_id}= #{result.inspect}" result end end class Object def visit(&block) ; block.call(self) ; end end arr = [ 10, [ 210 ], 30 ] it = arr.visit #=> Enumerable#visit: self=0x13928400= [10, [210], 30] #=> Enumerable#visit: result=0x13927776= # puts ; rr = it.each{ |x| x+7 } #=> Enumerable#visit: self=0x13928400= [10, [210], 30] #=> Enumerable#visit: self=0x13928416= [210] #=> Enumerable#visit: result=0x1562576= [217] #=> Enumerable#visit: result=0x13926848= [17, [217], 37] puts "#=> rr=0x#{rr.object_id}= #{rr.inspect}" #=> rr=0x13926848= [17, [217], 37] puts ; rr = it.map{ |x| x+7 } #=> Enumerable#visit: self=0x13928400= [10, [210], 30] #=> Enumerable#visit: self=0x13928416= [210] #=> Enumerable#visit: result=0x1559360= [nil] #=> Enumerable#visit: result=0x1559840= [nil, [nil], nil] puts "#=> rr=0x#{rr.object_id}= #{rr.inspect}" #=> rr=0x1561088= [17, 217, 37] # Note that the last Enumerable#visit result from it.map # has the same structure as the original nested array # (albeit with nil values - dunno why at the moment), # but that the return value of it.map is a flat array # of the successive results of the block calculation. Changing: if block_given? then result = map do |e| e.visit(&block) end to: if block_given? then result = each do |e| e.visit(&block) end gives for it.map: puts ; rr = it.map{ |x| x+7 } #=> Enumerable#visit: self=0x14484864= [10, [210], 30] #=> Enumerable#visit: self=0x14484896= [210] #=> Enumerable#visit: result=0x14484896= [210] #=> Enumerable#visit: result=0x14484864= [10, [210], 30] puts "#=> rr=0x#{rr.object_id}= #{rr.inspect}" #=> rr=0x1298112= [17, 217, 37] From: Intransition on 10 Apr 2010 14:05 On Apr 9, 6:38 pm, Colin Bartlett wrote:> I'm not sure that it is a bug. > > http://www.ruby-doc.org/ruby-1.9/classes/Enumerable.html#M002713 > enum.map {| obj | block } => array > Returns a new array with the results of running block once for every > element in enum. > > http://www.ruby-doc.org/ruby-1.9/classes/Enumerable.src/M002713.html > static VALUE > enum_collect(VALUE obj) > { >     VALUE ary; >     RETURN_ENUMERATOR(obj, 0, 0); >     ary = rb_ary_new(); >     rb_block_call(obj, id_each, 0, 0, collect_i, ary); >     return ary; > > } > > The following is Intransition's methods with some added info. > It looks to me as though two things are happening. > > 1. Using #map inside #visit combined with using Enumerable#map >    seems to (within #visit) produce the structure of the original array, >    but with nil values instead of the original values. >    (Changing #map inside #visit to #each gets back the original values.) > > 2. Irrespective of the internal results from #visit, Enumerable#map >    collects the successive results of the block for Enumerable#map >    into a new (flat) array. > >   module Enumerable >     def visit(&block) > puts "#=> Enumerable#visit: self=0x#{self.object_id}= #{self.inspect}" >       if block_given? then result = map do |e| e.visit(&block) end >       else result = to_enum(:visit) >       end > puts "#=> Enumerable#visit: result=0x#{result.object_id}= #{result.inspect}" >       result >     end >   end > >   class Object >     def visit(&block) ; block.call(self) ; end >   end > > arr = [ 10, [ 210 ], 30 ] > it = arr.visit > #=> Enumerable#visit: self=0x13928400= [10, [210], 30] > #=> Enumerable#visit: result=0x13927776= # > > puts ; rr = it.each{ |x| x+7 } > #=> Enumerable#visit: self=0x13928400= [10, [210], 30] > #=> Enumerable#visit: self=0x13928416= [210] > #=> Enumerable#visit: result=0x1562576= [217] > #=> Enumerable#visit: result=0x13926848= [17, [217], 37] > puts "#=> rr=0x#{rr.object_id}= #{rr.inspect}" > #=> rr=0x13926848= [17, [217], 37] > > puts ; rr = it.map{ |x| x+7 } > #=> Enumerable#visit: self=0x13928400= [10, [210], 30] > #=> Enumerable#visit: self=0x13928416= [210] > #=> Enumerable#visit: result=0x1559360= [nil] > #=> Enumerable#visit: result=0x1559840= [nil, [nil], nil] > puts "#=> rr=0x#{rr.object_id}= #{rr.inspect}" > #=> rr=0x1561088= [17, 217, 37] > > # Note that the last Enumerable#visit result from it.map > # has the same structure as the original nested array > # (albeit with nil values - dunno why at the moment), > # but that the return value of it.map is a flat array > # of the successive results of the block calculation. > > Changing: >       if block_given? then result = map do |e| e.visit(&block) end > to: >       if block_given? then result = each do |e| e.visit(&block) end > gives for it.map: > > puts ; rr = it.map{ |x| x+7 } > #=> Enumerable#visit: self=0x14484864= [10, [210], 30] > #=> Enumerable#visit: self=0x14484896= [210] > #=> Enumerable#visit: result=0x14484896= [210] > #=> Enumerable#visit: result=0x14484864= [10, [210], 30] > puts "#=> rr=0x#{rr.object_id}= #{rr.inspect}" > #=> rr=0x1298112= [17, 217, 37] Thanks, that helped me understand a little better. After working on this for far too long, I have concluded that a highly generalized implementation of recursion for all (or at least most) enumerable methods not feasible. But there are still many things in common between recursion methods, so instead I have created a Recursor class to act something like an Enumerator for recursive operations. Thanks for the help. First  |  Prev  |  Next  |  Last Pages: 1 2 3 4 5 6 Prev: what is the meaning of \A and \ZNext: win32ole question