2010年8月30日星期一

快速排序

看了《The Joy of Clojure》的快速排序,觉得比较丑,而且不通用。所以自己又写了一个。不过有一个比不上书里面的。书里面的支持 lazy-seq,而下面这个不支持。可能正是因为要支持 lazy-seq 所以书里面的那个才会那么繁琐吧……

(defn- split-by [pred coll]
[(filter pred coll) (remove pred coll)])

(defn qsort-by [comparator work]
(letfn [(step [ret [pivot & rst :as work]]
(if-not work
ret
(let [[left right] (split-by #(< (comparator % pivot) 0) rst)]
(if (seq left)
(recur ret (concat left [pivot] right))
(recur (conj ret pivot) rst)))))]
(seq (step [] (seq work)))))

(println (qsort-by - [57 66 72 27 16]))
(println (qsort-by - []))
(println (qsort-by - nil))

;=> (16 27 57 66 72)
;=> nil
;=> nil

快速排序(升序)我理解为:给定一个轴(pivot),遍历所有还没有排序完整的元素(rst),把小于轴的元素放到轴的左边(left),把不小于轴的元素放到轴的右边(right)。完全排序好的元素放到结果集(ret)里。如果左边有元素,说明找到了比轴还要小的元素。那么结果集不动,因为还没有找到最小的元素。如果左边没有元素,说明当前的轴就是最小的,把它加入结果集。

split-by 不同于 split-withsplit-with 在遇到第一个不满足条件的元素后就终止了。而 split-by 会把整个集合都遍历一遍(这里的实现是遍历了两遍)。

如果需要排序,请用 Clojure 核心库的 sort 函数。这个函数转调 java.util.Arrays.sort 函数,它是经过高度优化的。我的测试表明我的函数比标准库的慢了两三个数量级…… -_-!

4 条评论:

云中漫步 说...

你好,我最近也开始对clojure感兴趣,一直想看The Joy of Clojure,可是我一直找不到,不知道你是否介意发给我一份它的pdf。另外,很想认识你跟你讨教clojure及其他方面的编程问题。
QQ:566391,msn:liumengjiang@msn.com。

杨冬 说...

没有问题,虽然这么做不好,但是外国书真的很贵……

这本书前两周刚好所有章节都出完了,现在还有排版问题和错误,不过文本和例子不会再变动了。考虑到你可以看到我的博客,说明你在墙外,有钱的话还请到 manning 的官网买个电子版,很快的,大概 120 块人民币。等到正式版出来了他们还会再给你新的下载。

另外,如果是新学,建议先看《Programming Clojure》,比这本书系统。然后再看 Clojure 的 core.clj,把核心库看懂对于理解的帮助大到无法想象,至少我个人这么觉得。最后才是看这本书。因为这本书里面涉及到的东西非常杂。也正因为如此才有趣味~~~

云中漫步 说...

120元不是问题,但是我不知道怎么用人民币支付,希望能认识你。

杨冬 说...

前天我就加你msn了,你一直没上过线