Stack Overflow上有各种答案,它解释了Scala中尾递归的条件。我理解限制以及如何以及在哪里可以利用尾递归。我不明白的部分是为什么存在对私有或最终方法的限制。
我还没有研究Scala编译器如何在字节码级别将递归函数实际转换为非递归函数,但我们假设它执行类似下面的操作。我有一个带有递归函数Foo
的类mod
:
class Foo {
def mod(value: Int, denom: Int): Int = {
if(denom <= 0 || value <= 0) 0
else if(0 <= value && value < denom) value
else mod(value - denom, denom)
}
}
这是一个基本的模数函数,我想Scala编译器转换为某种伪Java-Scala,如:
class Foo {
def mod(value: Int, denom: Int): Int = {
if(denom <= 0 || value <= 0) return 0
while(value > denom) value -= denom
return value
}
}
(我可以相信我搞砸了那个翻译,但我不认为细节很重要。)
所以现在假设我是Foo
的子类:
class Bar extends Foo {
def mod(value:Int, denom: Int): Int = 1
}
是什么阻止了它的工作?当JVM调用Foo/Bar
并调用mod
时,为什么解析应该使用的mod
函数会出现问题。为什么这与基函数是非递归的情况有什么不同?
我可以看到的一些可能的原因是:
无论出于什么原因,Scala编译器的实现都没有处理这个问题(如果是这样的话就足够公平。如果是这样,是否有计划改变它?)
Foo
mod
函数在编译期间被mod-non-recursive
传递给Foo
,因此mod
实际上没有{{1}}方法来覆盖。
答案 0 :(得分:8)
我刚刚回答了这个问题,但让我们举个例子。假设您定义了类Foo,并将其作为JAR文件提供。
然后我得到那个Jar文件,并以这种方式扩展你的Foo:
class Bar extends Foo {
def mod(value:Int, denom: Int): Int = {
Logger.log("Received mod with "+value+" % "+denom)
super.mod(value, denom)
}
现在,当Foo的mod
调用自身时,因为我的对象是Bar
,而不是Foo
,你应该(并且确实)去 Bar's mod
,而不是Foo的。
因为这是真的,你无法按照你所展示的方式对其进行优化。
子类的合约是,当超类在自身上调用方法时,如果该方法被覆盖,则它将是要调用的子类'方法。
将方法声明为private,使其成为final或类 - 甚至创建递归函数而不是方法,所有这些都确保您不必去子类实现。
答案 1 :(得分:-1)
IttayD今天早些时候刚问过这个问题。答案是Foo的尾部递归只会在you can't override mod in subclasses时进行优化(因为该类是最终的,或者因为该方法是最终的或私有的。)