【发布时间】:2017-05-27 10:05:23
【问题描述】:
我知道这是一个反复出现的问题(here、here 等),我知道这个问题与创建惰性序列有关,但我不明白为什么它失败了。
问题:我编写了一个(不是很好的)快速排序算法来对使用循环/递归的字符串进行排序。但应用于 10000 个元素,我得到一个 StackOverflowError:
(defn qsort [list]
(loop [[current & todo :as all] [list] sorted []]
(cond
(nil? current) sorted
(or (nil? (seq current)) (= (count current) 1)) (recur todo (concat sorted current))
:else (let [[pivot & rest] current
pred #(> (compare pivot %) 0)
lt (filter pred rest)
gte (remove pred rest)
work (list* lt [pivot] gte todo)]
(recur work sorted)))))
我是这样用的:
(defn tlfnum [] (str/join (repeatedly 10 #(rand-int 10))))
(defn tlfbook [n] (repeatedly n #(tlfnum)))
(time (count (qsort (tlfbook 10000))))
这是堆栈跟踪的一部分:
[clojure.lang.LazySeq seq "LazySeq.java" 49]
[clojure.lang.RT seq "RT.java" 521]
[clojure.core$seq__4357 invokeStatic "core.clj" 137]
[clojure.core$concat$fn__4446 invoke "core.clj" 706]
[clojure.lang.LazySeq sval "LazySeq.java" 40]
[clojure.lang.LazySeq seq "LazySeq.java" 49]
[clojure.lang.RT seq "RT.java" 521]
[clojure.core$seq__4357 invokeStatic "core.clj" 137]]}
据我所知,循环/递归执行尾调用优化,因此不使用堆栈(实际上是使用递归语法编写的迭代过程)。
阅读其他答案,并且由于堆栈跟踪,我发现concat 存在问题,并在concat 解决堆栈溢出问题之前添加doall。但是……为什么?
【问题讨论】: