4

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有aFoo/Bar并且mod在上面调用的时候,为什么解析mod应该使用的函数会出现问题。为什么这与基函数非递归的情况有什么不同?

我认为出现这种情况的几个可能原因是:

  1. 无论出于何种原因,Scala 编译器的实现都无法处理这个问题(如果是这样的话,那就足够了。如果是这样,是否有计划改变这个?)

  2. in函数Foo在编译期间被修改,因此实际上没有方法可以覆盖。modmod-non-recursiveFoomod

4

2 回答 2

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)
}

现在,当 Foomod调用自身时,因为我的对象是 aBar而不是 a Foo,你应该(并且确实)去Bar's mod,而不是 Foo's。

因为这是真的,所以你不能按照你展示的方式优化它。

子类化的约定是,当超类调用自己的方法时,如果该方法已被覆盖,它将是要调用的子类的方法。

将方法声明为私有的,使其成为最终的,或者类——或者甚至创建一个递归函数而不是一个方法,所有这些都可以确保您不必去子类实现。

于 2009-11-09T20:25:55.327 回答
-1

IttayD 今天早些时候刚问过这个问题。答案是 Foo 的尾递归只有在你不能覆盖子类中的 mod 时才会被优化(因为类是最终的,或者因为方法是最终的或私有的。)

于 2009-11-09T19:23:25.677 回答