Can I reference a lambda from within itself using Ruby?

前提是你 提交于 2019-12-18 11:50:38

问题


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?


回答1:


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.




回答2:


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)



回答3:


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

minimal function




回答4:


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


来源:https://stackoverflow.com/questions/9516061/can-i-reference-a-lambda-from-within-itself-using-ruby

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!