我在Clojure中定义了以下函数。
; return the input unchanged
(defn same [x] x)
; Recursively call the function on the input N times
(defn recurse-n-times [input function n]
(if (= n 0)
input
(recurse-n-times (function input) function (- n 1))
)
)
以下是我的递归函数的一些输出:
(recurse-n-times 0 inc 5) ; returns 5, as expected
(recurse-n-times 0 same 5) ; returns 0, as expected
(recurse-n-times 0 same 5000) ; StackOverflowError:
; clojure.lang.Numbers$LongOps.combine
; (Numbers.java:394)
我不明白为什么我得到StackOverflowError
。 recurse-n-times
做的最后一件事就是调用自身,所以我希望它能使用尾递归而不会增长堆栈。
我希望期望这个替代定义能够提供StackOverflowError
:
(defn bad-recurse-n-times [input function n]
(if (= n 0)
input
(function (alt-recurse-n-times input function (- n 1)))
)
)
为什么recurse-n-times
不使用尾递归?
答案 0 :(得分:14)
这是JVM的限制,而不是Clojure的限制。 JVM不支持TCO。
Clojure为此提供了一个特殊表格,recur
(defn recurse-n-times [input function n]
(if (= n 0)
input
(recur (function input) function (- n 1))))
(recurse-n-times 0 same 500000) ;; will work
重复形式应出现在尾部位置,否则Clojure编译器会抱怨它。
请注意,recur是Clojure中唯一不占用堆栈的循环结构。没有尾调用优化,并且不鼓励使用自调用来循环未知边界。 recur是功能性的,它在尾部位置的使用由编译器验证。
答案 1 :(得分:4)