在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))))))
答案 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调用约定 不能也不会做同样的事情 尾调用优化保证。 相反,它提供了复现特殊 运算符,它做恒定空间 通过重新绑定和递归循环 跳到最近的封闭循环 或功能框架。虽然不如此 一般作为尾调用优化,它 允许大多数同样优雅 构造,并提供优势 检查重复的调用可以 只发生在尾部位置。