所謂尾遞歸,就是方法的遞歸調用在方法體的最後。scala編譯器會把尾遞歸的字節碼優化成循環執行的形式,但有一個限制可能會被你們忽略.先看一個例子:java
class Approx { def isGoodEnough(guess: Double): Boolean = if (guess < 1) true else false def improve(guess: Double): Double = guess - 1 @tailrec final def approximate(guess: Double): Double = if (isGoodEnough(guess)) guess else approximate(improve(guess)) def approximateLoop(initialGuess: Double): Double = { var guess = initialGuess while (!isGoodEnough(guess)) guess = improve(guess) guess } } object TailRecDemo { val app = new Approx() def main(args: Array[String]) = { println(System.getProperty("java.class.path")) recursive(1) iterate(1) recursive(10) iterate(10) recursive(100) iterate(100) } def recursive(n: Int) = { val start = java.lang.System.currentTimeMillis() for (i <- 0 to 10000000) { app.approximate(n) } println(java.lang.System.currentTimeMillis() - start) } def iterate(n: Int) = { val start = java.lang.System.currentTimeMillis() for (i <- 0 to 10000000) { app.approximateLoop(n) } println(java.lang.System.currentTimeMillis() - start) } }
下面是執行結果,能夠看出遞歸調用的方式比循環方式慢了不少,並且有次數越多慢的幅度越大的傾向。 shell
922 969 2406 2032 13578 8047
咱們對approximate加上@tailrec註釋,從新編譯。這時編譯器給出了錯誤:app
error: could not optimize @tailrec annotated method approximate: it is neither private nor final so can be overridden def approximate(guess: Double): Double =
原來對於class中定義的函數,若是沒有聲明爲final或private,那麼編譯器是不會對尾遞歸進行優化的。
Scala在尾遞歸的優化上有諸多限制,好比遞歸必須是直接遞歸而不能是間接的,也不能是經過函數對象調用實現的遞歸,假如scala能突破這些限制,那是一件很是振奮人心的事
函數