为什么 Scala 编译器不会应用尾调用优化,除非方法是最终的?

2023-12-20

为什么 Scala 编译器不会应用尾调用优化,除非方法是最终的?

例如,这个:

class C {
    @tailrec def fact(n: Int, result: Int): Int =
        if(n == 0)
            result
        else
            fact(n - 1, n * result)
}

结果是

错误:无法优化@tailrec注释的方法:它既不是私有的也不是最终的,因此可以被覆盖

如果编译器应用的话到底会出什么问题TCO http://en.wikipedia.org/wiki/Tail_call在这样的情况下?


考虑以下与 REPL 的交互。首先我们定义一个带有阶乘方法的类:

scala> class C {
         def fact(n: Int, result: Int): Int =
           if(n == 0) result
           else fact(n - 1, n * result)
       }
defined class C

scala> (new C).fact(5, 1)
res11: Int = 120

现在让我们在子类中重写它,使超类的答案加倍:

scala> class C2 extends C {
         override def fact(n: Int, result: Int): Int = 2 * super.fact(n, result)
       }
defined class C2

scala> (new C).fact(5, 1)
res12: Int = 120

scala> (new C2).fact(5, 1)

您对最后一次通话的预期结果是什么?您可能期望 240。但事实并非如此:

scala> (new C2).fact(5, 1)
res13: Int = 7680

这是因为当超类的方法进行递归调用时,递归调用会经过子类。

如果重写有效,使得 240 是正确答案,那么在此处的超类中执行尾部调用优化将是安全的。但这不是 Scala(或 Java)的工作原理。

除非一个方法被标记为final,它可能不会自称当它进行递归调用时。

这就是为什么 @tailrec 不起作用,除非方法是最终的(或私有的)。

更新:我建议您也阅读其他两个答案(约翰的和雷克斯的)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Scala 编译器不会应用尾调用优化,除非方法是最终的? 的相关文章

随机推荐