Why am I getting a stack overflow in the following Clojure function:
为什么我在以下Clojure函数中获得堆栈溢出:
(defn length
[xs]
(if ,(not= xs nil)
(println (+ 1 (length (rest xs))))
(println 0)))
2 个解决方案
#1
I think the idiomatic way of doing this is to call seq
on your collection. seq
on a collection returns nil
if the collection is empty.
我认为这样做的惯用方法是在你的收藏中调用seq。如果集合为空,则集合上的seq返回nil。
(defn length [xs]
(if (seq xs)
(inc (length (rest xs)))
0))
This isn't tail-recursive (you aren't using recur
and can't here) so this will still overflow the stack on very large collections.
这不是尾递归(你不使用recur而不能在这里)所以这仍然会在非常大的集合上溢出堆栈。
user> (println (length (range 1000000)))
;; stack overflow
One tail-recursive version would be
一个尾递归版本
(defn length [xs]
(loop [xs xs
acc 0]
(if (seq xs)
(recur (rest xs) (inc acc))
acc)))
user> (println (length (range 1000000)))
1000000
This won't overflow the stack even for huge collections but it's still slow. Many Clojure collections implement the Counted
interface and the built-in count
function returns the length of those collections in constant time.
即使对于大型集合,这也不会溢出堆栈,但它仍然很慢。许多Clojure集合实现了Counted接口,内置count函数以恒定时间返回这些集合的长度。
#2
After the switch to all lazy seqs, rest will never return nil, just an empty list - try this:
切换到所有懒惰的seqs后,休息将永远不会返回nil,只是一个空列表 - 试试这个:
(defn length
[xs]
(if (not (empty? xs))
(println (+ 1 (length (rest xs))))
(println 0)))
OR this
(defn length
[xs]
(if ,(not= xs nil)
(println (+ 1 (length (next xs))))
(println 0)))
#1
I think the idiomatic way of doing this is to call seq
on your collection. seq
on a collection returns nil
if the collection is empty.
我认为这样做的惯用方法是在你的收藏中调用seq。如果集合为空,则集合上的seq返回nil。
(defn length [xs]
(if (seq xs)
(inc (length (rest xs)))
0))
This isn't tail-recursive (you aren't using recur
and can't here) so this will still overflow the stack on very large collections.
这不是尾递归(你不使用recur而不能在这里)所以这仍然会在非常大的集合上溢出堆栈。
user> (println (length (range 1000000)))
;; stack overflow
One tail-recursive version would be
一个尾递归版本
(defn length [xs]
(loop [xs xs
acc 0]
(if (seq xs)
(recur (rest xs) (inc acc))
acc)))
user> (println (length (range 1000000)))
1000000
This won't overflow the stack even for huge collections but it's still slow. Many Clojure collections implement the Counted
interface and the built-in count
function returns the length of those collections in constant time.
即使对于大型集合,这也不会溢出堆栈,但它仍然很慢。许多Clojure集合实现了Counted接口,内置count函数以恒定时间返回这些集合的长度。
#2
After the switch to all lazy seqs, rest will never return nil, just an empty list - try this:
切换到所有懒惰的seqs后,休息将永远不会返回nil,只是一个空列表 - 试试这个:
(defn length
[xs]
(if (not (empty? xs))
(println (+ 1 (length (rest xs))))
(println 0)))
OR this
(defn length
[xs]
(if ,(not= xs nil)
(println (+ 1 (length (next xs))))
(println 0)))