Talk:Y combinator: Difference between revisions
Content added Content deleted
(→Is this really the "Y" combinator?: Ruby has not the Y combinator.) |
|||
Line 43: | Line 43: | ||
A quick glance over the different implementations shows that most of them define the Z combinator. But that is the closest you can get in applicative-order languages. The Z combinator is just one eta-conversion away from the Y combinator. So in lambda calculus these are basically the same: "two functions are the same if and only if they give the same result for all arguments". [[Special:Contributions/131.155.116.18|131.155.116.18]] 17:28, 30 November 2009 (UTC) |
A quick glance over the different implementations shows that most of them define the Z combinator. But that is the closest you can get in applicative-order languages. The Z combinator is just one eta-conversion away from the Y combinator. So in lambda calculus these are basically the same: "two functions are the same if and only if they give the same result for all arguments". [[Special:Contributions/131.155.116.18|131.155.116.18]] 17:28, 30 November 2009 (UTC) |
||
=== Ruby has not the Y combinator === |
|||
The current solution for [[Ruby]] is not a Y combinator. |
|||
<lang ruby># This is not the Y combinator, |
|||
# but it is a stateless fixed-point combinator, |
|||
# which is the same function as a Y combinator. |
|||
Y = lambda do |f| |
|||
lambda {|g| g[g]}[lambda do |g| |
|||
f[lambda {|*args| g[g][*args]}] |
|||
end] |
|||
end</lang> |
|||
If it was a Y combinator, it would not work. |
|||
<lang ruby># This is the Y combinator, |
|||
# but it does not work in Ruby. |
|||
# (Almost, the real Y combinator would |
|||
# start with f[g[g]] instead of g[g].) |
|||
Y = lambda do |f| |
|||
lambda {|g| g[g]}[lambda do |g| |
|||
f[g[g]] # This is an infinite recursion. |
|||
end] |
|||
end</lang> |
|||
Ruby has '''eager evaluation''', so f[g[g]] will evaluate g[g] before calling f. However, g[g] will evaluate f[g[g]] again, which will evaluate g[g] again. This is an infinite recursion, and the program will crash, by overflowing the call stack, without ever calling f. |
|||
But I can also do '''lazy evaluation''' in Ruby. I only need an extra <tt>lambda{g[g]}</tt> to delay the evaluation. |
|||
<lang ruby># This is a Y combinator that works in Ruby. |
|||
Y = lambda do |f| |
|||
lambda {|g| g[g]}[lambda do |g| |
|||
f[lambda {g[g]}] |
|||
end] |
|||
end |
|||
# This is factorial. |
|||
Fac = Y[lambda {|f| lambda {|n| n < 2 ? 1 : n * f[][n - 1]}}] |
|||
# extra brackets ^^ evaluate g[g]</lang> |
|||
The extra lambda requires an extra brackets. This is why my factorial function has <tt>f[][n - 1]</tt> and not <tt>f[n - 1]</tt>. The first [] evaluates g[g] (which returns the recursive factorial function), and the second [n - 1] calls the recursive factorial function. |
|||
However, I would prefer to write <tt>f[n - 1]</tt> without the extra brackets. The current solution uses <tt>lambda {|*args| g[g][*args]}</tt> to both evaluate g[g] (which returns the recursive function), and to evaluate the recursive function with *args. |
|||
<lang ruby># This is not the Y combinator, |
|||
# but it is a stateless fixed-point combinator, |
|||
# which is the same function as a Y combinator. |
|||
Y = lambda do |f| |
|||
lambda {|g| g[g]}[lambda do |g| |
|||
f[lambda {|*args| g[g][*args]}] |
|||
end] |
|||
end |
|||
# This is factorial. |
|||
Fac = Y[lambda {|f| lambda {|n| n < 2 ? 1 : n * f[n - 1]}}] |
|||
# no extra brackets! ^^</lang> |
|||
This is not the Y combinator, but it is almost the Y combinator. --[[User:Kernigh|Kernigh]] 03:34, 11 April 2011 (UTC) |
|||
==Could We remove the recursive examples!== |
==Could We remove the recursive examples!== |