尾部调用优化失败时 Clojure 警告/错误
在 Scala 2.8.x 中,添加了一个新注释 (@tailrec
),如果编译器无法对带注释的方法执行尾部调用优化,则会给出编译时错误。
Clojure 中是否有关于循环/递归的类似功能?
编辑: 在阅读了我的问题的第一个答案(感谢 Bozhidar Batsov)并在 Clojure 文档中进一步搜索后,我发现了这个:
(recur exprs*)
按顺序计算表达式,然后并行地将递归点的绑定重新绑定到表达式的值。如果递归点是 fn 方法,那么它会重新绑定参数。如果递归点是循环,则它会重新绑定循环绑定。然后执行跳回到递归点。递归表达式必须与递归点的元数完全匹配。特别是,如果递归点是可变参数 fn 方法的顶部,则不会收集其余参数 - 应传递单个 seq (或 null)。 在尾部位置以外的位置重复出现是错误。
请注意,recur 是 Clojure 中唯一不消耗堆栈的循环结构。没有尾部调用优化,并且不鼓励使用自调用来循环未知边界。 recur 是函数式的,它在尾部位置的使用由编译器验证 [强调是我的]。
(def factorial
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
(recur (dec cnt) (* acc cnt))))))
In Scala 2.8.x, a new annotation (@tailrec
) has been added that gives a compile-time error if the compiler cannot perform a tail-call optimization on the annotated method.
Is there some similar facility in Clojure with respect to loop/recur
?
EDIT:
After reading the first answer to my question (thanks, Bozhidar Batsov) and further searching in the Clojure docs, I came across this:
(recur exprs*)
Evaluates the exprs in order, then, in parallel, rebinds the bindings of the recursion point to the values of the exprs. If the recursion point was a fn method, then it rebinds the params. If the recursion point was a loop, then it rebinds the loop bindings. Execution then jumps back to the recursion point. The recur expression must match the arity of the recursion point exactly. In particular, if the recursion point was the top of a variadic fn method, there is no gathering of rest args - a single seq (or null) should be passed. recur in other than a tail position is an error.
Note that recur is the only non-stack-consuming looping construct in Clojure. There is no tail-call optimization and the use of self-calls for looping of unknown bounds is discouraged. recur is functional and its use in tail-position is verified by the compiler [emphasis is mine].
(def factorial
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
(recur (dec cnt) (* acc cnt))))))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
实际上 Scala 中的尾部调用优化情况与 Clojure 中的情况相同:在简单情况下可以执行,例如自递归,但在一般情况下则不行,例如调用任意函数功能位于尾部位置。
这是由于 JVM 的工作方式——为了让 TCO 在 JVM 上工作,JVM 本身必须支持它,但目前尚不支持(尽管当 JDK7 发布时这可能会改变) )。
请参阅此博客条目了解以下内容的讨论Scala 中的 TCO 和蹦床。 Clojure 具有完全相同的功能来促进非堆栈消耗(=尾调用优化)递归; 这包括当用户代码尝试在非尾部位置调用
recur
时抛出编译时错误。Actually the situation in Scala w.r.t. Tail Call Optimisation is the same as in Clojure: it is possible to perform it in simple situations, such as self-recursion, but not in general situations, such as calling an arbitrary function in tail position.
This is due to the way the JVM works -- for TCO to work on the JVM, the JVM itself would have to support it, which it currently doesn't (though this might change when JDK7 is released).
See e.g. this blog entry for a discussion of TCO and trampolining in Scala. Clojure has exactly the same features to facilitate non-stack-consuming (= tail-call-optimised) recursion; this includes throwing a compile-time error when user code tries to call
recur
in non-tail position.AFAIK 使用循环/递归时没有尾部调用优化。引用官方文档:
There is no tail-call optimization when you use loop/recur AFAIK. A quote from the official docs: