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)

关于堆栈溢出有各种各样的答案,它们解释了在这种情况下,尾部递归是可能的。我了解其局限性,以及如何以及在何处利用尾部递归。我不理解的部分是为什么存在对私有或最终方法的限制

我还没有研究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)
  }
}
是什么阻止了这一切?当JVM有一个
Foo/Bar
并对其调用
mod
时,为什么在解决应该使用的
mod
函数时会出现问题。为什么这与基函数是非递归的情况有什么不同

在这种情况下,我可以看到以下几个可能的原因:

  • 无论出于何种原因,Scala编译器的实现都无法处理这一问题(如果是这样的话,这是很公平的。如果是这样,是否有计划改变这一点?)

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