【问题标题】:Groovy: Why can't the closure access itself?Groovy:为什么闭包不能访问自己?
【发布时间】:2015-07-30 17:33:24
【问题描述】:

我是一个 Groovy 新手。当我运行以下脚本时,Groovy 报告“No such property: tailFactorial for class ***”。闭包不应该访问局部变量tailFactorial吗?

def factorial(int factorialFor) {
    def tailFactorial = { int number, BigInteger theFactorial = 1 ->
        number == 1 ? theFactorial :
        tailFactorial.trampoline(number - 1, number * theFactorial)
    }.trampoline()
    tailFactorial(factorialFor)
}
println "factorial of 5 is ${factorial(5)}"
println "Number of bits in the result is ${factorial(5000).bitCount()}"

让我困惑的是,如果我将上面的代码更改为以下代码:

def factorial(int factorialFor) {
    def tailFactorial
    tailFactorial = { int number, BigInteger theFactorial = 1 ->
        number == 1 ? theFactorial :
        tailFactorial.trampoline(number - 1, number * theFactorial)
    }.trampoline()
    tailFactorial(factorialFor)
}
println "factorial of 5 is ${factorial(5)}"
println "Number of bits in the result is ${factorial(5000).bitCount()}"

运行良好。

我们可以发现,两段代码的唯一区别是,第一段代码同时声明和定义了闭包,而第二段代码声明了没有定义的闭包。定义放在单独的一行中。

怎么会这样?我正在使用 Groovy 2.4.3 和 Java 7,期待您的帮助。谢谢。

【问题讨论】:

  • 只是因为在您的第一个代码 sn-p 中,tailFactorial 闭包在执行后还不存在,因此无法从其内部访问。
  • Val 是正确的,在第 4 行代码中,您在 tailFactorial 闭包变量的初始化程序中引用了 tailFactorial。由于初始化程序在声明完成之前执行,tailFactorial 变量还不存在,也不能被引用。在第二个例子中,变量是先创建的,没有初始化,所以可以在赋值闭包中引用。
  • @BillJames,我认为闭包声明是可以的,因为初始化执行时不会执行闭包的代码。就像Can't call method itself in grails 说的那样,这是一个技巧。我只是不知道这个技巧是如何工作的。

标签: groovy


【解决方案1】:

您无法访问将分配闭包的变量,因为它在右侧之后弹出存在(或者至少没有捕获get)。

这就是为什么第二个示例(与 groovy 文档中的代码相同)有效的原因。该变量已经被声明并且闭包可以通过这个名字来捕获它。 (请记住,它没有立即执行的闭包代码 - 在这个确切的时间点 tailFactorial 将是空的)。

但是由于您只对闭包上的蹦床调用感兴趣,因此您可以简单地在闭包本身上调用它。 trampolineClosure的方法:

def factorial(int factorialFor) {
    def tailFactorial = { int number, BigInteger theFactorial = 1 ->
        number == 1 ? theFactorial :
        // XXX
        trampoline(number - 1, number * theFactorial)
    }.trampoline()
    tailFactorial(factorialFor)
}
println "factorial of 5 is ${factorial(5)}"
println "Number of bits in the result is ${factorial(5000).bitCount()}"

【讨论】:

  • 您的解决方案确实有效,但我仍然想知道为什么第二个代码 sn-p 有效。谢谢。
  • @SamuelGong 我添加了关于捕获部分的更好(?)解释
  • 我认为你是对的。顺便说一下,Groovy 闭包的工作方式与 JavaScript 不同。例如,以下代码运行良好: var factorial = function(n) { if (n == 1) { return 1; } else { 返回 n * 阶乘(n - 1); } } 警报(阶乘(5));
  • 我最后的理解是,局部变量应该在声明闭包之前就存在,这样闭包就可以捕获它们然后使用它们。我说得对吗,@cfrick?
  • 乍一看,我在文档中找不到明确的声明,就是这样说的。但据我所知,groovy clojures 在其自身的分配中捕获了“高于”自身的变量,而不是 LHS。是的,你是对的。
猜你喜欢
  • 1970-01-01
  • 2019-09-07
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 2011-03-20
  • 2012-03-04
  • 2013-07-07
相关资源
最近更新 更多