我正在阅读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)
但它会生成以下堆栈跟踪
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)
这意味着它为每个递归调用生成一个新的堆栈帧。但最后一步是呼唤自己。什么是错的以及如何使其尾递归?
为什么编译器不进行尾调用优化?
答案 0 :(得分:9)
您可以尝试使用@tailrec
注释标记方法。如果你这样做,编译将失败,并将告诉你为什么编译器无法优化它是尾递归:
Main.scala:12:错误:无法优化@tailrec带注释的方法测试:它既不是私有的也不是最终的,所以可以被覆盖
实际上,如果你制作方法final
,它就会按预期工作。
答案 1 :(得分:2)
您的代码正常运行。 mc
对象中的值随着每一步而减少。在最后一步,您将获得异常。
您可以将函数的返回类型更改为Boolean
,并在值为false
时返回< 0
,否则返回true
。
我建议你使用'@tailrec'注释来检查编译器的递归函数调用。