Ruby has better support for lambdas than most “mainstream” languages, but it’s missing one important feature — a way to reference the lambda itself. Not being able to reference itself makes recursion using a lambda somewhat difficult. For example, say I wanted to define factorial using a recursive lambda. A lot of rubyists would suggest using self.
lambda{|n| n.zero? ? 1 : n * self.call(n-1)}
Unfortunately, self is evaluated in the context of the calling block, so it doesn’t return the lambda as you might hope; it returns main or whatever context you’re calling the lambda from, usually resulting in an error, or at least not what you were hoping for. The only way, as far as I can tell, to reference the lambda is by assigning it to a variable:
fac = lambda{|n| n.zero? ? 1 : n * fac.call(n-1)}
Again, this is not ideal. Not only does it pollute the calling context, it relies on the variable name it’s assigned to. It works, but it’s ugly. The only solution I’ve been able to come up with is to wrap it in another lambda:
# Ruby 1.8.7
lambda{|n| (fac=lambda{|n| n.zero? ? 1 : n * fac.call(n-1)}).call(n) }
# Ruby 1.9
->(n){(fac=->(n){n.zero? ? 1 : n * fac.(n-1)}).(n)}
This is better from a purity standpoint, but it’s still ugly code. I’ve heard talk of some way to reference the current block in upcoming versions of Ruby, but I haven’t been able to find any concrete information on the subject. If anyone has news, let me know.
Update 2008-10-06 20:42
Apparently what I was looking for here is the Y combinator. Nex3 does a far better job of explaining it than I could hope to, but here’s the code to make it happen:
def Y
lambda { |f| f.call(f) }.call(
lambda do |g|
yield(lambda { |*n| g.call(g).call(*n) })
end)
end
Y { |this| lambda { |n| n == 0 ? 1 : n * this.call(n - 1) } }.call(12) #=> 479001600
Thanks to sjs for pointing this out.
4 Comments until now
I’d like ruby’s support for lambdas better, if I didn’t have use “.call” to invoke one. I guess “.call” has to be there, because ruby is so relentlessly object oriented.
Here’s what I was able to make happen in irb:
lambda {|f,n| f.call(f,n)}.call(
lambda {|f,n| n.zero? ? 1: n * f.call(f, n-1)},
10
)
==> 3628800
The first lambda is sort of like a simplified Y combinator; the second one does the factorial. The binding done by applying the lambdas lets you avoid using assignment.
It’s prettier in scheme (IMHO):
(
(lambda (f n) (f f n))
(lambda (f n)
(if (zero? n)
1
(* n (f f (- n 1))) ))
10
)
Cheers!
George Kangas
Actually, you don’t need to use .call() in Ruby. In 1.8.7, you can use [], so:
lambda{|n|n*2}[2] #=> 4
In 1.9, you can also use .(), like so:
lambda{|n|n*2}.(2) #=> 4
You’re looking for the Y combinator.
http://en.wikipedia.org/wiki/Fixed_point_combinator#Y_combinator
The Y Combinator is “purely functional,” but you may like #rcall, which is a little closer to idiomatic Ruby:
http://github.com/raganwald/homoiconic/tree/master/2008-12-03/rcall.md
Add your Comment!