【问题标题】:Scala and tail recursionScala和尾递归
【发布时间】:2009-11-09 18:27:57
【问题描述】:

Stack Overflow 上有各种答案,解释了在Scala 中可以进行尾递归的条件。我了解限制以及如何以及在何处可以利用尾递归。我不明白的部分是为什么存在对私有或最终方法的限制。

我还没有研究过 Scala 编译器实际上是如何在字节码级别将递归函数转换为非递归函数的,但我们假设它执行以下操作。我有一个带有递归函数mod 的类Foo

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函数会出现问题。为什么这与基函数非递归的情况有什么不同?

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

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

  2. Foo 中,mod 函数在编译期间被转换为mod-non-recursive,因此Foo 实际上没有要覆盖的mod 方法。

【问题讨论】:

    标签: scala tail-recursion


    【解决方案1】:

    我刚刚回答了这个问题,但让我们以您自己为例。假设您定义了 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的。

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

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

    将方法声明为私有、将其设为最终方法或类 - 甚至创建递归函数而不是方法,所有这些都可确保您不必去实现子类。

    【讨论】:

      【解决方案2】:

      IttayD 今天早些时候刚问过这个问题。答案是 Foo 的尾递归只有在 you can't override mod in subclasses 时才会被优化(因为类是最终的,或者因为方法是最终的或私有的。)

      【讨论】:

      • @Ken Bloom:ams 知道方法必须是最终的或私有的,否则不会发生优化。他在问为什么。为什么 scalac 不优化可覆盖的方法?在这个 mod 的实现中并没有任何歧义——这个实现中的递归调用总是会调用这个实现。
      • 不正确。如果函数 [i] 可能[/i] 被覆盖,则为调用生成的代码必须是多态的。考虑到在这种情况下,您不知道第一次调用来自子类,而不是来自定义该方法的类的“外部”。
      • @Ken - OP 非常清楚地表示他了解限制(即私有/最终) - 他想了解其背后的原因
      猜你喜欢
      • 1970-01-01
      • 2018-03-17
      • 2018-03-08
      • 1970-01-01
      • 1970-01-01
      • 2017-04-10
      • 2014-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多