如果函数实际上是递归的,那么转换如下(这是理解TCO的编译器会为你做的,所以你不应该自己做):
原文:
function func(a1, a2, a3...)
... doesn't contain either return or call
return val
...
return func(x1, x2, x3...)
...
... etc.
转换为:
function func(a1, a2, a3...)
func: // label for goto (yuk!)
...
return val // No change
...
a1 = x1; a2 = x2; a3 = x3...; goto func;
...
... etc.
为了使这种转换与相互协同递归的函数一起工作,您需要将它们组合成一个函数,每个函数都带有一个标签。如上所述,简单的return 语句不会被更改,return foo(...) 会变成对参数变量的赋值,然后是goto foo。
当然,在组合函数时,您可能需要重命名局部变量以避免冲突。而且你也将失去使用多个顶级函数的能力,除非你在顶部入口点添加类似switch 语句(带有gotos),在任何标签之前。 (事实上,在允许goto case foo 的语言中,您可以只使用大小写标签作为标签。)
goto 的使用当然是丑陋的。如果您使用的语言最好保证尾调用优化,或者失败,至少做出合理的尝试并在失败时报告,那么绝对没有动机替换递归解决方案,(在我看来)几乎总是更具可读性。
在某些情况下,可以将 goto 和标签替换为 while (1) { ... }or other such loops, but that involves replacing thegotos withcontinue`(或等效项)之类的东西,如果它们不起作用,那将不起作用'嵌套在其他循环中。因此,您实际上可能会浪费大量时间来使丑陋的转换稍微不那么丑陋,但最终仍然无法获得像原始程序那样可读的程序。
我现在停止宣传递归。 :)
已编辑(我忍不住,抱歉)
这是 Lua 中的一个回溯 n-queens 解决方案(确实有 TCO),由一个尾递归求解器和一个尾递归验证器组成:
function solve(legal, n, first, ...)
if first == nil -- Failure
then return nil
elseif first >= n -- Back-track
then return solve(legal, n, ...)
elseif not legal(first + 1, ...) -- Continue search
then return solve(legal, n, first + 1, ...)
elseif n == 1 + select("#", ...) -- Success
then return first + 1, ...
else -- Forward
return solve(legal, n, 0, first + 1, ...)
end
end
function queens_helper(dist, first, second, ...)
if second == nil
then return true
elseif first == second or first - dist == second or first + dist == second
then return false
else
return queens_helper(dist + 1, first, ...)
end
end
function queens_legal(...) return queens_helper(1, ...) end
-- in case you want to try n-rooks, although the solution is trivial.
function rooks_legal(first, second, ...)
if second == nil then return true
elseif first == second then return false
else return rooks_legal(first, ...)
end
end
function queens(n) return solve(queens_legal, n, 0) end