Scala是否对递归进行了优化,以构造返回值列表?
我读过一些文章提到Scala有一个尾部递归优化 但是这个怎么样:Scala是否对递归进行了优化,以构造返回值列表?,scala,Scala,我读过一些文章提到Scala有一个尾部递归优化 但是这个怎么样: def f(i: Int): List[Int] = { if (i == 0) Nil else i :: f(i - 1) // Is there any optimizations? } 第5行不是尾部递归 Scala有什么优化吗?或者它可能比命令式版本占用更多的堆栈空间?使用scalaz trampoline object tail { def f(i: Int): Trampoline[L
def f(i: Int): List[Int] = {
if (i == 0)
Nil
else
i :: f(i - 1) // Is there any optimizations?
}
第5行不是尾部递归
Scala有什么优化吗?或者它可能比命令式版本占用更多的堆栈空间?使用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)
}
}
而且不会出现堆叠问题使用scalaz蹦床
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)
}
}
而且不会有堆栈问题,因为它目前的状态,这段代码将在一个单独的堆栈框架中计算每个递归。如果
i
足够大,则内存不足
scala> f(10000)
java.lang.StackOverflowError
at .f(<console>:15)
at .f(<console>:15)
at .f(<console>:15)
请注意,列表在返回之前的反转(
x.reverse
)是0(n)
(复杂度),但只有当您需要它的顺序明显相反时才有必要进行反转。由于它目前的状态,此代码将在单独的堆栈帧中计算每个递归。如果i
足够大,则内存不足
scala> f(10000)
java.lang.StackOverflowError
at .f(<console>:15)
at .f(<console>:15)
at .f(<console>:15)
请注意,在返回列表之前对列表进行反转(
x.reverse
)是0(n)
(复杂性),但只有当您显然需要以相反的顺序使用它时才有必要进行反转。当函数不是尾部递归时,函数将使用更多的堆栈空间。如果您的函数不能进行尾部调用优化,您可以使用函数上的@tailrec
注释使编译器给出错误。您好@Pavel,实际上这不是尾部递归。为了确认我刚刚添加了marstran评论中的注释。然后,编译器引发了一个错误:无法优化\@tailrec注释的方法f:它包含一个不在尾部位置的递归调用当函数不是尾部递归时,函数将使用更多堆栈空间。如果您的函数不能进行尾部调用优化,您可以使用函数上的@tailrec
注释使编译器给出错误。您好@Pavel,实际上这不是尾部递归。为了确认我刚刚添加了marstran评论中的注释。然后,编译器引发了一个错误:无法优化\@tailrec注释的方法f:它包含一个不在尾部位置的递归调用注意,@tailrec
仅确保该调用是递归的--即使没有它,编译器也会优化正确的代码这是一个仅在编译时报告是否可能的将计算限制在单个堆栈帧的范围内。另一个(可变)稍微更有效的解决方案是使用ListBuffer
,这真的可以将元素添加到列表中知道tail
是中的var
:
注意@tailrec
只确保调用是递归的——即使没有正确的调用,编译器也会优化适当的代码——这是一个只在编译时进行的报告是否可能将计算限制在单个堆栈帧的范围内。另一个(可变)稍微更有效的解决方案是使用ListBuffer
,这真的可以将元素添加到列表中知道tail
是中的var
:
不要使用蹦床,除非你100%确定你在做什么。虽然我不认为蹦床是解决Monad transformers之外堆栈问题的好方法,我迫切希望把这个答案恢复到0分。另一点是scalaz
和cats
蹦床将在flatMap
上执行自动suspend
,所以蹦床。suspend
调用是多余的,不要使用蹦床,除非你100%确定你在做什么。虽然我不认为蹦床是解决Monad transformers之外堆栈问题的好方法,我迫切希望把这个答案恢复到0分。另一点是scalaz
和cats
蹦床将在flatMap
上执行自动suspend
,因此蹦床.suspend
调用过多