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 } }
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.
>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 } > }
On Sun, 2005-12-18 at 00:41 +0900, Christian Neukirchen wrote: > men...@rydia.net writes:
> > 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.
> C'mon, let's do complete hylomorphisms. :-)
Hey, this is pretty good. It might be worth into turning into a gem.
Only thing is I don't know about to_a ... there's no real reason the result has to be an array versus some other type (I've got lazy streams in mind, at least).
>> 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.
>C'mon, let's do complete hylomorphisms. :-)
Had to look that one up. wikipedia says: a philosophical concept that highlights the significance of matter in the composition of being, regarding matter to be as essential to a being as its form.
....but I don't feel any more enightened than I was prior to looking it up. What does 'hylomophism' mean in this context? Most all of the google hits had something to do with philosophy.
pt...@aracnet.com (Phil Tomson) writes: > In article <m28xujrarx....@gmail.com>, > Christian Neukirchen <chneukirc...@gmail.com> wrote: >>men...@rydia.net writes:
>>> 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.
>>C'mon, let's do complete hylomorphisms. :-)
> Had to look that one up. wikipedia says: > a philosophical concept that highlights the significance of matter in the > composition of being, regarding matter to be as essential to a being as its > form.
> .....but I don't feel any more enightened than I was prior to looking it up. > What does 'hylomophism' mean in this context? Most all of the google hits had > something to do with philosophy.
I use the term hylomorphism in the context of category theory, where a hylomorphism is a compsite of an anamorphism (unfold) and an catamorphism (fold/inject).
> this is quite cool even if I'm not quite sure how hylomorphism applys.
> BTW: in regards to the other 'advanced Ruby book' threads recently, this is > just the sort of thing I'd like to see in a 'Higher Order Ruby' type book.
MenTaLguY <men...@rydia.net> writes: > On Sun, 2005-12-18 at 00:41 +0900, Christian Neukirchen wrote: >> men...@rydia.net writes:
>> > 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.
>> C'mon, let's do complete hylomorphisms. :-)
> Hey, this is pretty good. It might be worth into turning into a gem.
> Only thing is I don't know about to_a ... there's no real reason the > result has to be an array versus some other type (I've got lazy streams > in mind, at least).
True, #to_a is left-over. Not sure what it should be called, tho.