【问题标题】:Can I reference a lambda from within itself using Ruby?我可以使用 Ruby 从自身内部引用 lambda 吗?
【发布时间】: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。小心,它可能会炸毁你的大脑。

标签: ruby lambda block


【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 2010-10-03
      相关资源
      最近更新 更多