我能否在Ruby中使用lambda自身作为引用?

28

我希望能够在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中实现?


5
你是否熟悉Y组合器?它可能不是最佳的实际解决方案,但从理论角度来看非常有趣。如果你不熟悉,请看这篇文章(http://nex-3.com/posts/43-fun-with-the-y-combinator-in-ruby)。注意,它可能会让你大脑崩溃。 - KL-7
4个回答

13
在下面的例子中,Lambda仍然是匿名的,但它有一个引用。这是否算作匿名?
(l = lambda { l.call }).call

感谢 Niklas B. 指出我原始答案中的错误;我只在 IRB 中测试过它,并且那里可以工作。

当然,这会导致 SystemStackError: stack level too deep 错误,但它展示了目的。


6

看起来匿名函数确实没有引用。你可以通过callee进行检查。

lambda{ __callee__ }.call #=> nil

没有参数引用你不能调用这个函数。 我可以向您提供一种更加简洁的变体:
(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5)

1
最好还是为此创建一个命名函数,这样会更加简洁明了。 - Niklas B.
是的,我的原始版本确实将lambda用括号括起来,并在其中运行了.call()。然而,这并不是代码简洁性的问题,更多的是关于Ruby可以深入到函数式编程的兔子洞穴有多深。KL-7在上面有一条评论,链接到一篇描述Y组合器的非常有趣的文章。 - Edd Morgan

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

最简函数

几乎了。fact = -> (x){ x < 2 ? 1 : x*fact[x-1]} 少了一个非空格字符。 - pjs
3
大多数情况下,括号比方括号更常用。这是因为方括号通常表示访问数据(数组、哈希、结构等)。使用括号可以清楚地显示您正在调用一个方法/proc/lambda。方括号的使用并不是错误的,但可能会误导不知情的代码读者。 - 3limin4t0r

3

除了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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接