Created
November 16, 2010 13:20
-
-
Save glaforge/701818 to your computer and use it in GitHub Desktop.
Closure Trampoline Patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: groovy-core/src/main/groovy/lang/Closure.java | |
=================================================================== | |
--- groovy-core/src/main/groovy/lang/Closure.java (revision 21027) | |
+++ groovy-core/src/main/groovy/lang/Closure.java (revision ) | |
@@ -610,6 +610,39 @@ | |
return Memoize.buildSoftReferenceMemoizeFunction(protectedCacheSize, new LRUCache(maxCacheSize), this); | |
} | |
+ /** | |
+ * Executes the current closure on a functional trampoline. | |
+ * To prevent stack overflow due to deep recursion, functions can instead leverage the trampoline mechanism | |
+ * and avoid recursive calls altogether. Under trampoline, the function is supposed to perform one step of | |
+ * the calculation and, instead of a recursive call to itself or another function, it return back a new closure, | |
+ * which will be executed by the trampoline as the next step. | |
+ * Once a non-closure value is returned, the trampoline stops and returns the value as the final result. | |
+ * @param args Parameters to the closure, so as the trampoline mechanism can call it | |
+ * @return The final result returned by the last step of the trampolined calculation | |
+ */ | |
+ public Object trampoline(final Object... args) { | |
+ return this.curry(args).trampoline(); | |
+ } | |
+ | |
+ /** | |
+ * Executes the current closure on a functional trampoline. | |
+ * To prevent stack overflow due to deep recursion, functions can instead leverage the trampoline mechanism | |
+ * and avoid recursive calls altogether. Under trampoline, the function is supposed to perform one step of | |
+ * the calculation and, instead of a recursive call to itself or another function, it return back a new closure, | |
+ * which will be executed by the trampoline as the next step. | |
+ * Once a non-closure value is returned, the trampoline stops and returns the value as the final result. | |
+ * @return The final result returned by the last step of the trampolined calculation | |
+ */ | |
+ public Object trampoline() { | |
+ Closure currentFunction = this; | |
+ for(;;) { | |
+ final Object result = currentFunction.call(); | |
+ if (result instanceof Closure) { | |
+ currentFunction = (Closure) result; | |
+ } else return result; | |
+ } | |
+ } | |
+ | |
/* (non-Javadoc) | |
* @see java.lang.Object#clone() | |
*/ | |
Index: groovy-core/src/test/org/codehaus/groovy/runtime/trampoline/TrampolineTest.groovy | |
=================================================================== | |
--- groovy-core/src/test/org/codehaus/groovy/runtime/trampoline/TrampolineTest.groovy (revision ) | |
+++ groovy-core/src/test/org/codehaus/groovy/runtime/trampoline/TrampolineTest.groovy (revision ) | |
@@ -0,0 +1,27 @@ | |
+package org.codehaus.groovy.runtime.trampoline | |
+ | |
+/** | |
+ * | |
+ * @author Vaclav Pech | |
+ */ | |
+class TrampolineTest extends GroovyTestCase { | |
+ public void testFactorial() { | |
+ def fact | |
+ fact = {int n, BigInteger accumulator -> | |
+ n > 1 ? fact.curry(n - 1, n * accumulator) : accumulator | |
+ } | |
+ def factorial = {int n -> fact(n, 1)} | |
+ assert 1 == factorial.trampoline(1) | |
+ assert 2 == factorial.trampoline(2) | |
+ assert 6 == factorial.trampoline(3) | |
+ assert 402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == factorial.trampoline(1000) | |
+ } | |
+ | |
+ public void testMutualRecursion() { | |
+ def funB | |
+ def funA = {int num -> num == 0 ? 0 : funB.curry(num - 1)} | |
+ funB = {int num -> num == 0 ? 0 : funA.curry(num - 1)} | |
+ assert 0 == funA.trampoline(1000) | |
+ assert 0 == funA.trampoline(2000) | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment