我可以使用Ruby从内部引用lambda吗?

问题描述 投票:18回答:4

我希望能够使用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)

我知道这不起作用,因为selfmain对象。我做错了吗?我试图做一些不可能的事情 - 如果不是,这是因为某些理论上的限制还是仅仅是在Ruby中没有实现?

ruby lambda block
4个回答
8
投票

在下面的示例中,lambda仍然是匿名的,但它有一个引用。 (那是匿名吗?)

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

(感谢Niklas B.指出我原来的答案中的错误;我只在IRB中进行了测试,并在那里工作)。

这当然以SystemStackError: stack level too deep错误结束,但它证明了目的。


5
投票

似乎匿名函数确实没有任何参考。您可以通过callee查看

lambda{ __callee__ }.call #=> nil

如果没有参考,你就无法调用此功能。我可以向你求一个更干净的变体:

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

1
投票

除了KL-7's comment,这里还有一个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

使用y可以简化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

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

功能最小化

© www.soinside.com 2019 - 2024. All rights reserved.