Samuel Gong
Samuel Gong

Reputation: 157

Groovy: Why can't the closure access itself?

I'm a Groovy newbie. When I run the following script Groovy reports "No such property: tailFactorial for class ***". Shouldn't the closure access the local variable 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()}"

What makes me confused is that If I change the above code to the following one:

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()}"

It runs well.

We can find that the only difference between the two pieces of code is that in the first one we declare and define the closure at the same time but in the second one we declare the closure without definition. The definition goes in a separated line.

How could this happen? I'm using Groovy 2.4.3 and Java 7 and look forward to your help. Thanks.

Upvotes: 2

Views: 408

Answers (1)

cfrick
cfrick

Reputation: 37008

You can not access the variable, to which the closure will be assigned to, since it pops into existence after the right hand side (or at least get's not captured).

This is the reason, why the second example (the same code like in the groovy docs) works. The variable is already declared and the closure can capture it by this name. (remember, that the closure code it not executed at once - at this exact point in time tailFactorial would be null).

But since you are only interested in the trampoline call on the closure, you can simply call it on the closure itself. trampoline is a method of Closure:

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()}"

Upvotes: 5

Related Questions