尾调用优化失败时的Clojure警告/错误

时间:2010-04-26 11:08:19

标签: clojure tail-recursion

在Scala 2.8.x中,添加了一个新的注释(@tailrec),如果编译器无法对带注释的方法执行尾调用优化,则会产生编译时错误。

Clojure中是否存在与loop/recur相似的设施?

修改 在阅读了我的问题的第一个答案(谢谢,Bozhidar Batsov)并进一步搜索Clojure文档后,我发现了这个:

(recur exprs *)
按顺序计算exprs,然后并行地将递归点的绑定重新绑定到exprs的值。如果递归点是fn方法,那么它会重新引导params。如果递归点是一个循环,那么它会重新绑定循环绑定。执行然后跳回到递归点。 recur表达式必须与递归点的arity完全匹配。特别是,如果递归点是可变参数fn方法的顶部,则不会收集休息参数 - 应该传递单个seq(或null)。 在尾部位置以外的地方重复是一个错误

请注意,recur是Clojure中唯一不占用堆栈的循环结构。没有尾调用优化,并且不鼓励使用自调用来循环未知边界。 recur是功能性的,它在尾部位置的使用由编译器验证 [强调是我的]。

(def factorial
  (fn [n]
    (loop [cnt n acc 1]
       (if (zero? cnt)
            acc
          (recur (dec cnt) (* acc cnt))))))

2 个答案:

答案 0 :(得分:6)

实际上是Scala的情况w.r.t.尾调用优化与Clojure中的相同:可以在简单的情况下执行它,例如自递归,但在一般情况下不能,例如在尾部位置调用任意函数。

这是由于JVM的工作方式 - 为了让TCO在JVM上工作,JVM本身必须支持它,而目前它不支持它(尽管这可能会在JDK7发布了。)

参见例如this blog entry讨论TCO和Scala中的蹦床。 Clojure具有完全相同的功能,以促进非堆栈消耗(=尾部调用优化)递归; 这包括在用户代码尝试在非尾部位置调用recur时抛出编译时错误

答案 1 :(得分:4)

使用loop / recur AFAIK时没有尾调用优化。来自官方文档的引用:

  

在没有可变的本地   变量,循环和迭代必须   采取不同的形式   内置for或while的语言   由...控制的结构   改变国家。在功能上   语言循环和迭代是   通过递归替换/实现   函数调用。很多这样的语言   保证函数调用   尾部位置不消耗堆栈   空间,因此递归循环   利用恒定的空间。自Clojure   它使用Java调用约定   不能也不会做同样的事情   尾调用优化保证。   相反,它提供了复现特殊   运算符,它做恒定空间   通过重新绑定和递归循环   跳到最近的封闭循环   或功能框架。虽然不如此   一般作为尾调用优化,它   允许大多数同样优雅   构造,并提供优势   检查重复的调用可以   只发生在尾部位置。