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.
Anyone got some better ideas?
-mental