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

Mar*_*ark 3 recursion clojure

为什么我在以下Clojure函数中获得堆栈溢出:

(defn length
  [xs]
  (if ,(not= xs nil)
    (println (+ 1 (length (rest xs))))
    (println 0)))
Run Code Online (Sandbox Code Playgroud)

Bri*_*per 10

我认为这样做的惯用方法是调用seq你的收藏. 如果集合为空,则seq返回nil集合.

(defn length [xs]
  (if (seq xs)
      (inc (length (rest xs)))
      0))
Run Code Online (Sandbox Code Playgroud)

这不是尾递归(你没有使用recur,也不能在这里)所以这仍然会在非常大的集合上溢出堆栈.

user> (println (length (range 1000000)))
;; stack overflow
Run Code Online (Sandbox Code Playgroud)

一个尾递归版本

(defn length [xs]
  (loop [xs xs
         acc 0]
    (if (seq xs)
        (recur (rest xs) (inc acc))
        acc)))

user> (println (length (range 1000000)))
1000000
Run Code Online (Sandbox Code Playgroud)

即使对于大型集合,这也不会溢出堆栈,但它仍然很慢.许多Clojure集合实现了Counted接口,内置count函数以恒定的时间返回这些集合的长度.