Groovy for Domain-specific Languages - Second Edition

(nextflipdebug2) #1
Chapter 5

[ 109 ]

fg(7) == f(7*7*7)
fg(13) == (2*(13*13*13)+4)
and:
gf(5) == g(f(5))
gf(7) == g(7*2+4)
gf(13) == (13*2+4)*(13*2+4)*(13*2+4)

Closure trampoline


Recursive method calls are notorious for causing stack overflow errors. Closures
are no different. If we implement a recursive algorithm in a closure, then in all
likelihood there is a limit to how deep it can be called before we get a stack overflow.
For example, an algorithm to calculate factorials might be written as follows:


def factorial
factorial = { BigDecimal n ->
println "Called"
if (n < 2)
1
else
n * factorial(n - 1)
}
factorial(1)
factorial(1000)
factorial(100000000)

Eventually, this will cause a StackOverflowError no matter how big our stack
is. To overcome this problem, Groovy 1.8 introduced the concept of a closure
trampoline. We create a trampoline by calling the trampoline method of the
closure. Calls to the trampolined closure are invoked sequentially until the
closure returns something other than another trampoline:


given: "a factorial algorithm using trampoline()"
def factorial
factorial = { int n, BigDecimal accumulator = 1 ->
if (n < 2)
accumulator
else
factorial.trampoline(n - 1, n * accumulator)
}
and: "we use trampoline() to wrap the closure"
factorial = factorial.trampoline()
expect: "it correctly calculates factorials"
factorial(1) == 1
factorial(3) == 1*2*3
Free download pdf