Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I reference a lambda from within itself using Ruby?

Tags:

ruby

lambda

block

I want to be able to call an anonymous lambda from within itself using Ruby. Consider the following recursive block (returns a factorial). I know I can assign it to a variable, and that variable is within the scope of the lambda:

fac = lambda { |n| n == 1 ? 1 : n * fac.call(n - 1) }
fac.call(5)

But, I want to be able to do the following (for no practical reason as of yet, I'm just interested in exploring the language some more):

(lambda { |n| n == 1 ? 1 : n * self.call(n - 1) }).call(5)

I know that won't work, because self is the main object. Am I doing it wrong? Am I trying to do something that's not possible -- and if not, is this because of some theoretical restriction or is it simply not implemented in Ruby?

like image 298
Edd Morgan Avatar asked Mar 01 '12 12:03

Edd Morgan


People also ask

How does Lambda work in Ruby?

In Ruby, a lambda is an object similar to a proc. Unlike a proc, a lambda requires a specific number of arguments passed to it, and it return s to its calling method rather than returning immediately.

Does Ruby have lambda?

In Computer Programming, Lambda functions are anonymous functions. Lambda functions in Ruby are no different. Since everything in Ruby is treated as an object, lambdas are also objects in Ruby. Lambdas in Ruby allow us to wrap data and logic in a portable package.


4 Answers

In the following example, the lambda is still anonymous, but it has a reference. (Does that pass for anonymous?)

(l = lambda { l.call }).call 

(Thanks to Niklas B. for pointing out the error in my original answer; I had only tested it in IRB and it worked there).

This of course ends in a SystemStackError: stack level too deep error, but it demonstrates the purpose.

like image 177
mydoghasworms Avatar answered Sep 30 '22 02:09

mydoghasworms


It seems that anonymous function really doesn't have any reference. You can check it by callee

lambda{ __callee__ }.call #=> nil 

And without reference you can't call this function. I can propose to you only a little more clean variant:

(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5) 
like image 26
megas Avatar answered Sep 30 '22 04:09

megas


fact = -> (x){ x < 2 ? 1 : x*fact.(x-1)}

minimal function

like image 22
Tommaso Mascioli Avatar answered Sep 30 '22 02:09

Tommaso Mascioli


In addition to KL-7's comment, here's a Y combinator solution:

lambda { |f|
  lambda { |x| x.call(x) }.call(
  lambda { |x| f.call( lambda { |v| x.call(x).call(v) } ) } )
}.call(
  lambda { |f|
    lambda { |n| n == 0 ? 1 : n * f.call(n - 1) }
  }
).call(5) #=> 120

You would normally split these:

y = lambda { |f|
  lambda { |x| x.call(x) }.call(
  lambda { |x| f.call( lambda { |v| x.call(x).call(v) } ) } )
}

fac = y.call(
  lambda { |f| lambda { |n| n == 0 ? 1 : n * f.call(n - 1) } }
)

fac.call(5) #=> 120

Note that although fac is being assigned, it is not used within the lambda.

I'd use Ruby's -> syntax and .() instead of .call():

y = ->(f) {
  ->(x) { x.(x) }.(
  ->(x) { f.(->(v) { x.(x).(v) }) } )
}

fac = y.(->(f) {
  ->(n) { n == 0 ? 1 : n * f.(n - 1) }
})

fac.(5) #=> 120

The y invocation can be simplified a bit by using curry:

y = ->(f) {
  ->(x) { x.(x) }.(
  ->(x) { f.curry.(->(v) { x.(x).(v) }) } )
}

fac = y.(
  ->(f, n) { n == 0 ? 1 : n * f.(n - 1) }
)

fac.(5) #=> 120
like image 38
Stefan Avatar answered Sep 30 '22 03:09

Stefan