为什么我在以下Clojure函数中获得堆栈溢出:
(defn length
[xs]
(if ,(not= xs nil)
(println (+ 1 (length (rest xs))))
(println 0)))
答案 0 :(得分:10)
我认为这样做的惯用方法是在你的收藏中调用seq
。如果集合为空,集合上的seq
将返回nil
。
(defn length [xs]
(if (seq xs)
(inc (length (rest xs)))
0))
这不是尾递归(你没有使用recur
而不能在这里)所以这仍然会在非常大的集合上溢出堆栈。
user> (println (length (range 1000000)))
;; stack overflow
一个尾递归版本将是
(defn length [xs]
(loop [xs xs
acc 0]
(if (seq xs)
(recur (rest xs) (inc acc))
acc)))
user> (println (length (range 1000000)))
1000000
即使对于大量收藏,这也不会溢出堆栈,但它仍然很慢。许多Clojure集合实现Counted
接口,内置count
函数以恒定时间返回这些集合的长度。
答案 1 :(得分:4)
切换到所有懒惰的seq后,休息将永远不会返回nil,只是一个空列表 - 试试这个:
(defn length
[xs]
(if (not (empty? xs))
(println (+ 1 (length (rest xs))))
(println 0)))
或者这个
(defn length
[xs]
(if ,(not= xs nil)
(println (+ 1 (length (next xs))))
(println 0)))