据说某些VM(最著名的是JVM)不支持TCO。结果,像Clojure这样的语言要求用户改用loop
recur
。
但是,我可以重写自尾调用以使用循环。例如,这是一个尾部调用阶乘:
def factorial(x, accum):
if x == 1:
return accum
else:
return factorial(x - 1, accum * x)
这是等效的循环:
def factorial(x, accum):
while True:
if x == 1:
return accum
else:
x = x - 1
accum = accum * x
这可以由编译器完成(我已经编写了执行此操作的宏)。为了相互递归,您可以简单地内联您正在调用的函数。
因此,假设您无需任何虚拟机就可以实现TCO,那么为什么语言(例如Clojure,Armed Bear Common Lisp)不这样做呢?我错过了什么?
最佳答案
出于多种原因,内联不能解决一般的尾部调用消除问题。以下列表并非详尽无遗。但是,它是经过排序的-它以不便开始,并以完整的topstopper结尾。
g
中有多个对f
的尾部调用。根据内联的常规定义,您必须在每个调用站点上内联g
,这可能会使f
变得很大。相反,如果您选择goto
到g
的开头然后跳回,则您需要记住要跳转到的位置,突然之间您要维护自己的调用堆栈片段(与之相比,几乎肯定会表现出较差的性能)。 “真实”调用堆栈)。 f
和g
,您必须内联f
中的g
和g
中的f
。显然,在通常的内联定义下这是不可能的。因此,剩下的是有效的自定义函数内调用约定(如上述2中基于goto
的方法一样)。 (defn say-foo-then-call [f]
(println "Foo!")
(f))
在某些情况下可以使用此方法产生很大的效果,并且显然不能用内联来模拟。
关于clojure - 为什么TCO需要VM的支持?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23175459/