Scala是否对返回值构造列表的递归进行了优化?

时间:2016-12-02 09:16:20

标签: scala

我已经阅读过帖子,提到Scala有尾递归优化。

但是这个怎么样:

def f(i: Int): List[Int] = {
  if (i == 0)
    Nil
  else
    i :: f(i - 1) // Is there any optimizations?
}

第5行不是尾递归。

Scala对它有一些优化吗?或者它可能会消耗更多的堆栈空间然后它的命令式版本?

2 个答案:

答案 0 :(得分:3)

目前,此代码将计算单独堆栈帧中的每个递归。如果i足够大,那么就会耗尽内存。

scala> f(10000)
java.lang.StackOverflowError
  at .f(<console>:15)
  at .f(<console>:15)
  at .f(<console>:15)

因此编译器无法帮助提高效率。

你可以防止i太大(如果它适用于你的要求,但也许不是很好),或者让它尾递归,这样它就可以在恒定的堆栈空间中计算。

作为一个例子。

scala> @tailrec def f(i: Int, x: List[Int] = List()): List[Int] = {
     |   if (i == 0)
     |     x.reverse
     |   else
     |     f(i - 1, i :: x)
     | }

f: (i: Int, x: List[Int])List[Int]

scala> f(10)
res10: List[Int] = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

请注意,在返回列表(x.reverse)之前,它的反转是0(n)(复杂性),但只有在你需要它时才显然需要它。

答案 1 :(得分:2)

使用scalaz trampoline

 object tail {

 def f(i: Int): Trampoline[List[Int]] = {
   if (i == 0)
     Trampoline.done(Nil)
   else
     Trampoline.suspend(f(i - 1)).flatMap { list =>
       Trampoline.done((i :: list))
     }
 }

 def main(args: Array[String]): Unit  = {
   println(f(1000).run)
 }

}

并且不存在堆栈问题