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

Posted

技术标签:

【中文标题】我可以使用 Ruby 从自身内部引用 lambda 吗?【英文标题】:Can I reference a lambda from within itself using Ruby? 【发布时间】:2012-03-19 22:31:59 【问题描述】:

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

【问题讨论】:

你熟悉 Y 组合器吗?这可能不是最好的实际解决方案,但从理论的角度来看,它非常有趣。如果您不这样做,请查看this article。小心,它可能会炸毁你的大脑。 【参考方案1】:

在以下示例中,lambda 仍然是匿名的,但它有一个引用。 (这是否通过匿名?)

(l = lambda  l.call ).call

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

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

【讨论】:

【参考方案2】:

看来匿名函数真的没有任何引用。您可以通过callee查看它

lambda __callee__ .call #=> nil

如果没有引用,您将无法调用此函数。 我只能向你建议一个更干净的变体:

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

【讨论】:

为它创建一个命名函数真的会干净得多。 是的,我的原始版本确实将 lambda 包含在括号中,我在那里运行了 .call() 。不过,这实际上并不是代码简洁的问题,更多的是关于 Ruby 的功能性兔子洞可以走多深。 KL-7 上面有一条评论链接到一篇描述 Y 组合子的文章,这是一篇非常有趣的文章。【参考方案3】:
fact = -> (x) x < 2 ? 1 : x*fact.(x-1)

最小函数

【讨论】:

差不多。 fact = -&gt; (x) x &lt; 2 ? 1 : x*fact[x-1] 少了一个非空白字符。 @pjs 大多数时候括号比方括号更受欢迎。仅仅因为方括号主要表示对数据的访问(数组、哈希、结构等)。使用括号清楚地表明您正在调用方法/proc/lambda。方括号的用法没有错,但可能会误导不知情的代码阅读者。【参考方案4】:

除了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 的 -&gt; 语法和 .() 而不是 .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

【讨论】:

以上是关于我可以使用 Ruby 从自身内部引用 lambda 吗?的主要内容,如果未能解决你的问题,请参考以下文章

lambda 函数可以在 Python 中递归调用自身吗?

lambda与匿名内部类

对自身内部列表的递归引用[重复]

有啥理由不使用全局 lambda?

引用计数 - 内部引用问题

对如何在 ruby​​ 中使用 lambda 进行选择感到困惑