你总是可以构建一个递归函数来消除尾部调用吗?如果没有,那么限制堆栈大小的其他策略是什么?
例如: (受Break or shortcircuit a fold in Scala启发)
// Depth-first search of labyrinth, with large depth > stacklimit
def search ( labyrinth: SearchDomain,
path: List[SolutionNode],
goal: DesiredResult ) = {
if ( path.head == goal ) return path
candidates: List[SolutionNode] = labyrinth.childNodes(path)
candidates.find { c =>
Nil != search( labyrinth, c :: path, goal ) // potential boom!
} match {
case Some(c) => c :: path
case None => Nil
}
}
目标不是挑选这个特定的功能,而是将其用作学习限制堆栈大小的技术。
更新
我对此的看法是:
如果问题域是递归可能会限制堆栈大小:
将代码重写为scala-compiler-version-of-tailcall-optimizable。这可以通过新的(2.8)@scala.annotation.tailrec注释来辅助/验证。
如果这不可行,请将其重写为使用迭代循环结构。
我也感觉到这种重写(无论是哪种情况)都需要一定的技能/才能/智慧/练习。
答案 0 :(得分:9)
所有递归函数都可以表示为迭代过程,并且所有迭代过程都可以表示为尾递归,因此,严格来说,您可以将任何递归算法转换为尾递归算法。但是,不要以为这实际上会节省你的空间。在许多情况下,堆栈完成的记录保存是必要的,并且您最终需要在迭代或尾递归版本中基本上模拟堆栈。这可能仍然有用,比如当你有一个小堆但是堆很大时。
答案 1 :(得分:4)
您应接受Laurence Gonsalves回答,但代码为:
// Depth-first search of labyrinth, with large depth > stacklimit
def search ( labyrinth: SearchDomain,
path: List[SolutionNode],
goal: DesiredResult ) = {
if ( path.head == goal ) return path
candidates: List[SolutionNode] = labyrinth.childNodes(path)
candidates.find { c =>
Nil != search( labyrinth, c :: path, goal ) // potential boom!
} match {
case Some(c) => c :: path
case None => Nil
}
}
变为
// Depth-first search of labyrinth
def search ( labyrinth: SearchDomain,
path: List[SolutionNode],
goal: DesiredResult ) = {
def recurse( candidates: List[List[SolutionNode]],
path: List[SolutionNode] ) = candidates match {
case List(Nil) => Nil
case Nil :: tail => recurse(tail, path.tail)
case (nextCandidate :: otherCandidates) :: tail =>
if (nextCandidate == goal)
nextCandidate :: path
else
recurse(labyrinth.childNodes(nextCandidate :: path) :: otherCandidates,
nextCandidate :: path)
}
if ( path.head == goal )
path
else
recurse(labyrinth.childNodes(path), path)
}
答案 2 :(得分:1)
我认为所有递归函数都不能表示为尾递归。
但是,您可以将所有递归函数表示为迭代过程。
答案 3 :(得分:1)
这里有两种情况需要考虑。在一般情况下,是否有一些递归函数不能表示为尾调用? [更新]正如另一个答案所指出的那样,没有。
但是,在 scala 的特定情况下,有一些尾递归函数无法优化以尾递归方式运行(意味着它们重用堆栈帧。)这主要是由于我相信JVM的局限性。
请参阅上一页question,了解有关其工作原理的详细信息。