用于理解时,递归调用不在尾部位置

时间:2013-10-05 14:08:50

标签: scala tail-recursion

我不知道我做错了什么或者它只是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)
  }
}

我相信上面的代码片段在语义上是相同的,并且两者都应该与尾递归一起使用。

1 个答案:

答案 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,它没有本机尾调用支持。