【问题标题】:getting a stack overflow in Clojure function.在 Clojure 函数中出现堆栈溢出。
【发布时间】:2009-07-16 17:02:25
【问题描述】:

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

(defn length
  [xs]
  (if ,(not= xs nil)
    (println (+ 1 (length (rest xs))))
    (println 0)))

【问题讨论】:

  • 你不要在 StackOverflow 上谈论堆栈溢出。大声笑
  • 不知道 clojure,但是当 xs 只有 1 个元素时,“rest xs”会返回 nil 吗?还是会返回一个空列表/字符串/任何不是零的东西?
  • 我认为堆栈只是因为预期的原因而溢出。 Clojure 不会自动优化尾调用(虽然这不是尾调用,但可以轻松地将其重写为一个)。
  • 我也很难相信这是在函数式语言中实现长度函数的最佳方式,对我来说它看起来非常糟糕。

标签: recursion clojure


【解决方案1】:

我认为这样做的惯用方法是在您的收藏中调用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

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

【讨论】:

    【解决方案2】:

    切换到所有惰性序列后,rest 将永远不会返回 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)))
    

    【讨论】:

      猜你喜欢
      • 2015-12-21
      • 2019-05-18
      • 1970-01-01
      • 2012-01-30
      • 1970-01-01
      • 2020-08-20
      • 2019-01-23
      • 2017-10-20
      相关资源
      最近更新 更多