容易被忽略的类成员方法的尾递归优化限制

        所谓尾递归,就是方法的递归调用在方法体的最后。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能突破这些限制,那是一件很是振奋人心的事

函数

相关文章
相关标签/搜索