Scala有智能编译器吗?

4
我写了一个递归函数,就像这样:
require : L (List[Int])

L模式匹配

  1. Nil => Thread.dumpStack()
  2. x :: xs => print(x) + function(xs)
def function(L : List[Int]) {
    L match {
        case Nil => Thread.dumpStack()
        case x :: xs => print(x + " "); function(xs)
    }
}

val l = (1 to 5).toList // function(l)

因此,我认为该函数在堆栈帧中多次出现,但实际上只出现了一次。我认为该函数已经找到了Nil并打印出异常Thread.dumpStack

Scala编译器是否聪明还是其他什么原因呢?


请更清晰地表达:提供一个简短的代码示例和REPL转录以说明问题。 - retronym
2个回答

7
你正在观察尾递归:从一次迭代到下一次,没有任何需要存储的内容,因此编译器将递归本质上转换为while循环。(所以,是的,编译器在这方面很聪明。)

1
正如Rex Kerr所指出的那样,这是Scala编译器应用尾调用优化。如果您想知道最终编译了什么,可以使用额外的参数运行编译器:
scalac -Xprint:tailcalls yourfile.scala

这将在tailcalls编译器阶段后打印中间表示形式。(如果您想了解所有阶段,也可以运行scalac -Xshow-phases。)例如,在以下输入上:

object TailRec {
  def foo(l : List[Int]) : Unit = l match {
    case Nil => Thread.dumpStack()
    case x :: xs => println(x); foo(xs)
  }
}

编译器将会输出(对于函数foo):
def foo(l: List[Int]): Unit = {
  <synthetic> val _$this: TailRec.type = TailRec.this;
  _foo(_$this,l){
    l match {
      case immutable.this.Nil => java.this.lang.Thread.dumpStack()
      case (hd: Int, tl: List[Int])collection.immutable.::[Int]((x @ _), (xs @ _)) => {
        scala.this.Predef.println(x);
        _foo(TailRec.this, xs)
      }
    }
  }
}

_foo(_$this,l) 这部分看起来像是一个函数定义,但实际上它是一个标签,“调用” _foo(TailRec.this, xs) 实际上是跳转到该标签。简而言之,编译器将递归调用重写为真正的 while 循环。

编译器会在可能的情况下自动应用优化。如果您想确保函数被正确重写,可以使用 @tailrec 进行注释,如果编译器无法进行优化,则会产生错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接