所谓尾递归,就是方法的递归调用在方法体的最后。scala编译器会把尾递归的字节码优化成循环执行的形式,但有一个限制可能会被大家忽略.先看一个例子:
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)
}
}
下面是执行结果,可以看出递归调用的方式比循环方式慢了很多,而且有次数越多慢的幅度越大的倾向。
922
969
2406
2032
13578
8047
我们对approximate加上@tailrec注释,重新编译。这时编译器给出了错误:
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能突破这些限制,那是一件非常振奋人心的事