主要问题:我认为尾部调用优化(TCO)的最重要应用是将递归调用转换为循环(在递归调用具有某种形式的情况下)。更准确地说,当翻译成机器语言时,通常就是翻译成一系列的跳跃。一些编译为本地代码(例如SBCL)的Common Lisp和Scheme编译器可以识别尾递归代码并执行此转换。Clojure和ABCL等基于JVM的Lisps很难做到这一点。JVM作为防止或使这种困难变得很难的机器是什么?我不明白 JVM显然对循环没有问题。是编译器必须弄清楚如何进行TCO,而不是要编译到的计算机。
相关问题:Clojure可以将看似递归的代码转换成一个循环:如果程序员用关键字替换函数的尾调用,它就好像在执行TCO recur
。但是,如果有可能让编译器识别出尾调用(例如SBCL和CCL那样),那么为什么Clojure编译器不能弄清楚它应该以对待尾调用的方式来对待recur
?
(很抱歉,这无疑是一个常见问题解答,我敢肯定,上面的评论显示出我的无知,但我未能找到较早的问题。)
真正的TCO适用于尾部位置的任意调用,而不仅仅是自我调用,因此如下代码不会导致堆栈溢出:
(letfn [(e? [x] (or (zero? x) (o? (dec x))))
(o? [x] (e? (dec x)))]
(e? 10))
显然,您需要为此提供JVM支持,因为在JVM上运行的程序无法操纵调用堆栈。(除非您愿意建立自己的调用约定并在函数调用上加上相关的开销; Clojure的目的是使用常规的JVM方法调用。)
至于消除尾部位置的自调用,这是一个更简单的问题,只要将整个函数体编译为单个JVM方法即可解决。但是,那是一个有限的承诺。此外,recur
它的明确性也颇受好评。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句