clojure中的插入排序会引发StackOverFlow错误

时间:2012-03-22 00:35:20

标签: clojure stack-overflow insertion-sort

(defn insert [s k]
    (let [spl (split-with #(< % k) s)]
       (concat (first spl) (list k) (last spl))))

(defn insert-sort [s]
    (reduce (fn [s k] (insert s k)) '() s))

(insert-sort (reverse (range 5000)))

抛出堆栈超过流错误。我在这里做错了什么?

3 个答案:

答案 0 :(得分:3)

Recursive function causing a stack overflow相同的问题。 Concat构建了一堆嵌套的延迟序列,如(concat(concat ...)))而不做任何实际的工作,然后当你强制第一个元素时concat s必须立即解决,吹掉堆栈。

答案 1 :(得分:2)

您的reduce每次都会创建新列表。

我的实施:

(defn- insert [el seq]
  (if (empty? seq) (cons el seq)
      (if (< el (first seq)) (cons el seq)
          (cons (first seq) (insert el (rest seq))))))

(defn insertion-sort
  ([seq sorted]
     (if (empty? seq) sorted
         (recur (rest seq) (insert (first seq) sorted))))
  ([seq]
     (insertion-sort seq nil)))

答案 2 :(得分:1)

正如主要答案所示,名单连字是罪犯。使用该列表作为输入调用“doall”将导致ISeq:

   ;;insertion sort helper
    (defn insert [s k]
        ;;find the insert point
        (let [spl (split-with #(< % k) s)
              ret (concat (first spl) (list k) (last spl))]
              (doall ret)))

    ;;insertion sort 
    (defn insert-sort [s]
        (reduce (fn [s k] (insert s k)) '() s))

但等等......序列是否仍然是懒惰的?

以下有关上述代码的黑客表明序列确实仍然是懒惰的!

;;insertion sort helper
(defn insert [s k]
    ;;find the insert point
    (let [spl (split-with #(< % k) s)
          ret (concat (first spl) (list k) (last spl))
          ret2 (doall ret)
          _ (println "final " (.getClass ret2))]
           ret2))

;;insertion sort 
(defn insert-sort [s]
    (reduce (fn [s k] (insert s k)) '() s))

所以,如果列表仍然是懒惰的,为什么使用doall修复了什么?

“doall”函数不能保证返回“非懒惰”列表,而是保证它返回的列表将通过完整的评估进行评估。

因此,问题的本质是多个函数调用,懒惰肯定与原始问题中代码的这个方面有关,但它不是溢出的“主要”来源。