为什么函数尾递归不是?

时间:2016-08-30 09:31:23

标签: scala recursion tail-recursion

我正在阅读M. Odersky的Scala编程,他说

  

近似的函数,将自己称为最后一个   动作,称为尾递归。

所以,我试过这个:

object Main extends App {
    implicit val mc = new MyClass(8)
    val ti = new TestImplct
    ti.test
}

class TestImplct {
  def test(implicit mc : MyClass): Unit = {
    println(mc.i)
    mc.i -= 1
    if(mc.i < 0){
      throw new IllegalArgumentException
    }
    test
  }
}

class MyClass(var i : Int)

IDEONE DEMO

但它会生成以下堆栈跟踪

 Exception in thread "main" java.lang.IllegalArgumentException
    at TestImplct.test(Main.scala:13)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)

这意味着它为每个递归调用生成一个新的堆栈帧。但最后一步是呼唤自己。什么是错的以及如何使其尾递归?

为什么编译器不进行尾调用优化?

2 个答案:

答案 0 :(得分:9)

您可以尝试使用@tailrec注释标记方法。如果你这样做,编译将失败,并将告诉你为什么编译器无法优化它是尾递归:

  

Main.scala:12:错误:无法优化@tailrec带注释的方法测试:它既不是私有的也不是最终的,所以可以被覆盖

实际上,如果你制作方法final,它就会按预期工作。

答案 1 :(得分:2)

您的代码正常运行。 mc对象中的值随着每一步而减少。在最后一步,您将获得异常。

您可以将函数的返回类型更改为Boolean,并在值为false时返回< 0,否则返回true

我建议你使用'@tailrec'注释来检查编译器的递归函数调用。