From: Intransition on 7 Apr 2010 17:49 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. My nearest success thus far is: require 'facets/functor' module Enumerable def recursive Functor.new do |op,*a,&b| __send__(op) do |e| if self.class === e e.recursive.__send__(op,*a,&b) else b.call(e) end end end end end This works for #each and #map but not #sort. Can anyone think of a better solution?
From: David Masover on 7 Apr 2010 23:51 On Wednesday 07 April 2010 04:49:15 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, Yes! > or barring that a Functor, What's a Functor in this context? We already have proc objects, which is traditionally what "functor" means... > This works for #each and #map but not #sort. #sort isn't a method of Enumerable, it's a method of Array. > Can anyone think of a better solution? Probably, but I'm not sure what it would look like. What's your use case -- how would I actually use this to, say, generate an infinite stream of fibbonacci numbers? I guess I don't really see why you'd define it in terms of an enumerable (in that case, you probably want inject).
From: Intransition on 8 Apr 2010 02:01 On Apr 7, 11:51 pm, David Masover <ni...(a)slaphack.com> wrote: > On Wednesday 07 April 2010 04:49:15 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, > > Yes! > > > or barring that a Functor, > > What's a Functor in this context? We already have proc objects, which is > traditionally what "functor" means... Functor is defined as: class Functor private(*instance_methods.select { |m| m !~ /(^__|^binding$)/ }) def initialize(&function) @function = function end def to_proc @function end def method_missing(op, *args, &blk) @function.call(op, *args, &blk) end end #method_missing is what makes it different from a Proc. > > This works for #each and #map but not #sort. > > #sort isn't a method of Enumerable, it's a method of Array. It seems to be according to ri. > > Can anyone think of a better solution? > > Probably, but I'm not sure what it would look like. What's your use case -- > how would I actually use this to, say, generate an infinite stream of > fibbonacci numbers? I don't think anyone can generate that seeing that it's infinite and all ;-) > I guess I don't really see why you'd define it in terms of an enumerable (in > that case, you probably want inject). Perhaps Array is the more appropriate place for it.
From: Intransition on 9 Apr 2010 11:01 On Apr 7, 5:49 pm, Intransition <transf...(a)gmail.com> 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? Here's the code for #visit: module Enumerable def visit(&block) if block_given? map do |e| e.visit(&block) end else to_enum(:visit) end end end class Object def visit(&block) block.call(self) end end class String # This is needed for Ruby 1.8 def visit(&block) block.call(self) end end
From: Robert Dober on 9 Apr 2010 14:03
On Thu, Apr 8, 2010 at 5:51 AM, David Masover <ninja(a)slaphack.com> wrote: > On Wednesday 07 April 2010 04:49:15 pm Intransition wrote: <snip> > >> 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. HTH R. -- Learning without thought is labor lost; thought without learning is perilous. --- Confucius |