我可以使用Ruby从内部引用lambda吗?
我希望能够使用Ruby从内部调用匿名lambda。 考虑以下递归块(返回阶乘)。 我知道我可以将它分配给变量,并且该变量在lambda的范围内:
fac = lambda { |n| n == 1 ? 1 : n * fac.call(n - 1) } fac.call(5)
但是,我希望能够做到以下(因为没有实际原因,我只是对探索语言感兴趣):
(lambda { |n| n == 1 ? 1 : n * self.call(n - 1) }).call(5)
我知道那是行不通的,因为self
是main
对象。 我做错了吗? 我是否尝试做一些不可能的事情 – 如果不是,这是因为某些理论上的限制还是仅仅是在Ruby中没有实现?
在以下示例中,lambda仍然是匿名的,但它有一个引用。 (那是匿名传递吗?)
(l = lambda { l.call }).call
(感谢Niklas B.指出我原来的答案中的错误;我只在IRB中进行过测试,并在那里工作)。
这当然以SystemStackError: stack level too deep
错误结束,但它certificate了目的。
似乎匿名函数确实没有任何参考。 您可以通过被叫方查看
lambda{ __callee__ }.call #=> nil
如果没有参考,你就无法调用此function。 我可以向你求一个更干净的变体:
(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5)
除了KL-7的评论 ,这里还有一个Y组合解决方案:
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
你通常会拆分这些:
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
请注意,虽然正在分配fac
,但它不在lambda中使用。
我使用Ruby的->
语法和.()
而不是.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
使用curry
可以简化y
调用:
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