From: Devin Mullins Date: 2005-12-17T09:53:12+09:00 Subject: Re: multiple blocks (unfold) mental@rydia.net wrote: >Anyone got some better ideas? > > The only other idea I can think of at the moment is one I've seen in a lot of Java DSLs (and in FlexMock): class Unfolder class << self def stop? &block new.stop? &block end def f &block new.f &block end def g &block new.g &block end end # reasonable defaults? def initialize @p = lambda {|s| !s} @f = lambda {|s| s} @g = lambda {|s| s.succ} end def stop?(&b) @p = b; self end def f(&b) @f = b; self end def g(&b) @g = b; self end def unfold(s) arr = [] until @p.call(s) arr << @f.call(s) s = @g.call(s) end arr end end Usage: a = Unfolder.stop? { |s| s > 10 }.f { |s| s * 2 }.g { |s| s + 1 }.unfold(0) Completely untested, and full of duplication, but you get the idea. Devin >I've been pondering how to write an "unfold" in Ruby lately, and >I've not really found any non-awkward ways to do it yet. > >For those not familiar, unfold is basically the inverse of foldl >(Ruby's inject); in its native form (as seen in most functional >languages), it takes four arguments: > > - an initial state > > - a predicate (tests the state to know when to stop) > > - a transformer (converts a state to an output value) > > - an incrementor (converts a state to the next state) > >It would look something like: > > def Array.unfold( s, p, f, g ) > arr = [] > until p.call( s ) > arr << f.call( s ) > s = g.call( s ) > end > arr > end > >You'd use it something like: > > a = Array.unfold( 0, lambda { |s| s > 10 }, lambda { |s| s * 2 }, >lambda { |s| s + 1 } ) > >That's pretty ugly in Ruby terms, though. The nicest way I've >thought of so far would be something like: > > class Unfolder > def initialize &block > (class << self > def self.stop? &block > define_method :stop?, &block > end > def self.f &block > define_method :f, &block > end > def self.g &block > define_method :g, &block > end > self > end).class_eval &block > end > > # reasonable defaults? > def stop?( s ) ; s ; end > def f( s ) ; s ; end > def g( s ) ; g.succ ; end > end > > def Array.unfold( s, &block ) > unfolder = Unfolder.new &block > arr = [] > until unfolder.stop? s > arr << unfolder.f s > s = unfolder.g s > end > arr > end > >Which could be used something like: > > a = Array.unfold( 0 ) { > stop? { |s| s > 10 } > f { |s| s * 2 } > g { |s| s + 1 } > } > >But that's kinda icky and probably slow-like. > > >-mental > > > > >