为什么Clojure有“复发”特殊形式?
当我用函数本身替换“recur”时,我得到相同的结果:
(defn print-down-from [x]
(when (pos? x)
(println x)
(recur (dec x))
)
)
(print-down-from 5)
与
具有相同的结果(defn print-down-from [x]
(when (pos? x)
(println x)
(print-down-from (dec x))
)
)
(print-down-from 5)
我想知道“recur”是否只是一个安全措施,因此如果开发人员碰巧使用非尾递归,编译器将抛出错误。或者编译器可能需要优化尾递归?
但我最想知道的是关于堆叠消费以外的任何其他原因。
答案 0 :(得分:12)
如the clojure.org page on functional programming中所述:
在没有可变局部变量的情况下,循环和迭代必须采用与内置for或while结构的语言不同的形式,这些结构由更改状态控制。在函数式语言中,循环和迭代通过递归函数调用替换/实现。 许多此类语言保证在尾部位置进行的函数调用不会占用堆栈空间,因此递归循环使用常量空间。由于Clojure使用Java调用约定,因此它不能也不会产生相同的尾调用优化保证。相反,它提供了recur特殊运算符,它通过重新绑定和跳转到最近的封闭循环或函数帧来执行常量空间递归循环。虽然不像尾部调用优化那样通用,但它允许大多数相同的优雅结构,并且提供了检查对recur的调用只能在尾部位置发生的优势。
当您不使用recur
(或trampoline
)时,您的函数调用使用堆栈:因此,如果您运行(print-down-from 100000)
,您很快就会发现崩溃。< / p>
然后,提供这种语言功能可以带来以下好处:
recur
不会消耗堆栈。最后,有关背景信息,请参阅有关StackOverflow主题的其他几个问题之一:
recur
。)答案 1 :(得分:0)
据我所知,loop .. recur
使用尾端递归,因此你的程序不会破坏堆栈,而常规递归则不会。 4clojure问题的一些解决方案最终没有使用loop .. recur
,因为 - 我采取了有根据的猜测 - 解决方案只能使用直接的递归函数调用来导出,而不是{{ 1}}。
据我所读,在几年前的一些Clojure书中,你应该随意使用loop .. recur
。
但是,至少从我阅读过的那些书中的讨论以及我在此处收到的答案中我已经收到的答案,我们已经达成了一些共识,首先尝试使用像loop .. recur
这样的结构来解决您的问题。如果这是不可能的,那么一定要继续使用map
,如果你不认为有可能会破坏你的堆栈,那就是直接的递归调用。