Scala和尾部递归
关于堆栈溢出有各种各样的答案,它们解释了在这种情况下,尾部递归是可能的。我了解其局限性,以及如何以及在何处利用尾部递归。我不理解的部分是为什么存在对私有或最终方法的限制 我还没有研究Scala编译器实际上是如何在字节码级别将递归函数转换为非递归函数的,但假设它执行以下操作。我有一个类Scala和尾部递归,scala,tail-recursion,Scala,Tail Recursion,关于堆栈溢出有各种各样的答案,它们解释了在这种情况下,尾部递归是可能的。我了解其局限性,以及如何以及在何处利用尾部递归。我不理解的部分是为什么存在对私有或最终方法的限制 我还没有研究Scala编译器实际上是如何在字节码级别将递归函数转换为非递归函数的,但假设它执行以下操作。我有一个类Foo,带有递归函数mod: class Foo { def mod(value: Int, denom: Int): Int = { if(denom <= 0 || value <= 0)
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)
}
}
是什么阻止了这一切?当JVM有一个Foo/Bar
并对其调用mod
时,为什么在解决应该使用的mod
函数时会出现问题。为什么这与基函数是非递归的情况有什么不同
在这种情况下,我可以看到以下几个可能的原因:
Foo
中,编译过程中将mod
函数调整为mod non recursive
,因此Foo
实际上没有要重写的mod
方法今天早些时候我刚刚问了这个问题。答案是,Foo的尾部递归只有在以下情况下才会优化(因为类是final的,或者因为方法是final或private的。)我刚才已经回答了这个问题,但让我们以您自己的例子为例。假设您定义了该类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的mod
,而不是Foo的
因为这是真的,所以你不能像你所展示的那样优化它
子类化的契约是,当一个超类对其自身调用一个方法时,如果该方法已被重写,它将是要调用的子类的方法
将方法声明为私有、将其设置为最终或类,甚至将其设置为递归函数而不是方法,所有这些都可以确保您不必使用子类实现。@Ken Bloom:ams知道方法必须是最终或私有的,否则不会发生优化。他在问这是为什么。为什么scalac不优化可重写的方法?mod的这个实现中并没有任何模糊性——这个实现中的递归调用总是调用这个实现。不是这样。如果函数[i]可能被重写,则为调用生成的代码必须是多态的。在这种情况下,你不知道第一个调用来自一个子类,而不是来自定义该方法的类的“外部”。
class Bar extends Foo {
def mod(value:Int, denom: Int): Int = 1
}
class Bar extends Foo {
def mod(value:Int, denom: Int): Int = {
Logger.log("Received mod with "+value+" % "+denom)
super.mod(value, denom)
}