我不知道我做错了什么或者它只是Scala编译器的属性 - 当我尝试编译这段代码时,我得到了提到的编译错误:
@tailrec
def shiftDown2(x: Int, bound: Int) {
val childOfX = chooseChild(x, bound)
for (child <- childOfX) {
swap(x, child)
shiftDown2(child, bound)
}
}
虽然以下代码编译没有问题:
@tailrec
def shiftDown(x: Int, bound: Int) {
val childOfX = chooseChild(x, bound)
if (childOfX.isDefined) {
swap(x, childOfX.get)
shiftDown(childOfX.get, bound)
}
}
我相信上面的代码片段在语义上是相同的,并且两者都应该与尾递归一起使用。
答案 0 :(得分:6)
尾递归优化不适用于for
循环内的递归调用,因为这里的for
循环只是调用foreach
高阶方法的语法糖。所以,你的代码相当于:
@tailrec
def shiftDown2(x: Int, bound: Int) {
val childOfX = chooseChild(x, bound)
childOfX.foreach { child =>
swap(x, child)
shiftDown2(child, bound)
}
}
scalac
只有在递归方法直接调用自身时才能优化尾调用 - 通过将其转换为类似于字节码中while
循环的东西。
不幸的是,这不是这种情况 - shiftDown2
调用childOfX.foreach
传递一个匿名函数。然后,foreach
(可能)在该匿名函数上调用apply
,并且匿名函数最终再次调用shiftDown2
。所以这是间接递归,无法通过scalac
进行优化。这种限制源于JVM,它没有本机尾调用支持。