我在clojure中编写了一个快速排序函数,但运行速度非常慢。有时,如果输入集合变得更大,它甚至可能溢出堆栈?
我的代码有什么问题吗?
(defn qsort [coll]
(if (<= (count coll) 1)
coll
(let [pivot (last coll)
head-half (filter #(< % pivot) (drop-last coll))
tail-half (filter #(>= % pivot) (drop-last coll))]
(concat (qsort head-half) (vector pivot) (qsort tail-half)))))
我也读过clojure.core中的sort函数,但它让人感到困惑。
01 (defn sort
02 "Returns a sorted sequence of the items in coll. If no comparator is
03 supplied, uses compare. comparator must
04 implement java.util.Comparator."
05 {:added "1.0"
06 :static true}
07 ([coll]
08 (sort compare coll))
09 ([^java.util.Comparator comp coll]
10 (if (seq coll)
11 (let [a (to-array coll)]
12 (. java.util.Arrays (sort a comp))
13 (seq a))
14 ())))
递归发生的唯一地方是第12行。它只是交换两个输入参数! 你可以向我解释为什么这段代码会运行吗?
答案 0 :(得分:2)
(. java.util.Arrays (sort a comp))
这个调用是Arrays排序函数,而不是对clojure.core排序函数的递归调用。
你的代码很慢,因为快速排序是imperative algorithm
,即它基于命令式编程的概念,如就地数组变异。您的代码很好,但问题是它遍历集合多次,并且还创建了许多中间集合。
您可以使用数组和就地数组变异来快速排序。
答案 1 :(得分:2)
Clojure的sort函数在内部使用Java的标准java.util.Arrays / sort方法;它不是100%的clojure实现。
Quicksort并不是真正的惯用法,因为它取决于具有快速O(1)交换元素的集合类型。还要注意,在你的实现中,你正在做(last coll)和(count coll)每次调用,其中coll是一个懒惰的seq,所以两者都是O(n) - 你应该能够通过这个来改进性能考虑〜可能是通过使用java数组而不是不可变的seq作为中间集合类型。
答案 2 :(得分:1)
堆栈溢出的问题是你是递归地在过滤器上放置延迟过滤器。这在那里解释得很好:
Recursive function causing a stack overflow
关于你的另一个问题:在第12行中,没有调用clojure sort函数,而是调用Arrays-sort函数。在用户代码中,您通常会编写(java.util.Arrays/sort a comp)
而不是点语法。
答案 3 :(得分:1)
您的qsort可能会花费大部分时间来分配对象。
虽然你的版本在我看来读得更好