在Scala 2.8.x中,增加了一个新的注解(@tailrec),如果编译器无法对注释方法执行尾调用优化,则会产生编译时错误。
在Clojure中是否有类似于loop/recur的功能?
编辑:阅读了我的问题的第一个答案(感谢Bozhidar Batsov)并在Clojure文档中进一步搜索后,我发现了这个:
(recur exprs*)按顺序评估exprs,然后并行重新绑定递归点的绑定到exprs的值。如果递归点是fn方法,则重新绑定参数。如果递归点是循环,则重新绑定循环绑定。然后执行跳回递归点。recur表达式必须与递归点的arity完全匹配。特别地,如果递归点是变长fn方法的顶部,则不会收集rest args - 应传递单个seq(或null)。非尾位置的recur是错误的。
请注意,recur是Clojure中唯一不消耗堆栈的循环结构。没有尾调用优化,不鼓励使用自调用进行未知边界的循环。recur是功能性的,其在尾位置的使用由编译器验证。
在Clojure中是否有类似于loop/recur的功能?
编辑:阅读了我的问题的第一个答案(感谢Bozhidar Batsov)并在Clojure文档中进一步搜索后,我发现了这个:
(recur exprs*)按顺序评估exprs,然后并行重新绑定递归点的绑定到exprs的值。如果递归点是fn方法,则重新绑定参数。如果递归点是循环,则重新绑定循环绑定。然后执行跳回递归点。recur表达式必须与递归点的arity完全匹配。特别地,如果递归点是变长fn方法的顶部,则不会收集rest args - 应传递单个seq(或null)。非尾位置的recur是错误的。
请注意,recur是Clojure中唯一不消耗堆栈的循环结构。没有尾调用优化,不鼓励使用自调用进行未知边界的循环。recur是功能性的,其在尾位置的使用由编译器验证。
(def factorial
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
(recur (dec cnt) (* acc cnt))))))
recur
指示Clojure编译器调用是尾递归,而@tailrec
询问Scala编译器调用是否为尾递归。因为Scala通常无法保证调用是尾递归(例如对于非最终方法),所以使用Clojure可以获得更多的尾递归优化,但需要显式声明。 - Peter Niederwieser@tailrec
应该会在被注释的方法无法转换为循环时导致编译器出错。相关文档页面似乎证实了这一点(一个带有@tailrec
注释的非尾递归阶乘方法的快速实验也是如此)。Clojure的recur
也会导致相同的行为(非尾位置的recur
是一个错误,并导致编译器抱怨)。虽然我不是Scala专家(远远不是!),但也许我在这里漏掉了一些重要的细节...? - Michał Marczyk@tailrec
时如何工作的情况下发表了任何评论;尽管如此,为了完全公正地对待Scala,说明注释是不必要的可能会更好,所以感谢您的评论。 - Michał Marczyk