在Clojure函数中获得堆栈溢出。

时间:2021-07-12 22:51:33

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)))