【发布时间】:2014-09-13 13:59:50
【问题描述】:
我想让一些函数针对尾递归进行优化。该函数会在没有优化的情况下发出 stackoverflow 异常。
示例代码:
import scala.util.Try
import scala.annotation.tailrec
object Main {
val trials = 10
@tailrec
val gcd : (Int, Int) => Int = {
case (a,b) if (a == b) => a
case (a,b) if (a > b) => gcd (a-b,b)
case (a,b) if (b > a) => gcd (a, b-a)
}
def main(args : Array[String]) : Unit = {
testTailRec()
}
def testTailRec() {
val outputs : List[Boolean] = Range(0, trials).toList.map(_ + 6000) map { x =>
Try( gcd(x, 1) ).toOption.isDefined
}
outputTestResult(outputs)
}
def outputTestResult(source : List[Boolean]) = {
val failed = source.count(_ == false)
val initial = source.takeWhile(_ == false).length
println( s"totally $failed failures, $initial of which at the beginning")
}
}
运行它将产生以下输出:
[info] Running Main
[info] totally 2 failures, 2 of which at the beginning
因此,前两次运行没有进行优化,并且由于 stackoveflow 异常而中途放弃,只有后面的调用才会产生所需的结果。
有一个解决方法:在实际使用之前,您需要通过假运行来预热该功能。但这似乎很笨拙且非常不方便。是否有任何其他方法可以确保我的递归函数在第一次运行之前针对尾递归进行优化?
更新:
有人告诉我使用两步定义
@tailrec
def gcd_worker(a: Int, b: Int): Int = {
if (a == b) a
else if (a > b) gcd(a-b,b)
else gcd(a, b-a)
}
val gcd : (Int,Int) => Int = gcd_worker(_,_)
如果可能的话,我更喜欢保持简洁的函数式定义。
【问题讨论】:
-
我想知道这是否会有所帮助。显式设置更大的堆栈大小。见stackoverflow.com/questions/20030120/java-default-stack-size 和stackoverflow.com/questions/3700459/…
-
这无济于事。只需将 6000 替换为更大的常数,您仍然会遇到同样的问题。尾递归优化将递归转换为迭代。我正在寻找这个
-
FWIW,
val foo = { def f = ??? ; f }是一个常见的成语。顺便说一句,tailrec 是编译时,但您似乎在谈论运行时编译,tl;dr.
标签: scala tail-recursion