在回答SO中的一个问题时,我偶然发现了这个问题:
(def x [7 4 8 9 10 54 55 2 23 30 12 5])
(defn insert-x
([sorted-coll x]
(insert-x sorted-coll x
(if (= (type sorted-coll) clojure.lang.PersistentVector) [] '())))
([sorted-coll x acc]
(let [is-vector (= (type sorted-coll) clojure.lang.PersistentVector)
format-it #(into (if is-vector [] '()) %)
compare (if is-vector < >)]
(cond
(empty? sorted-coll) (format-it (cons x acc))
(compare (peek sorted-coll) x)
(format-it (concat
((if is-vector identity reverse) sorted-coll)
(conj acc x)))
:else (recur (pop sorted-coll) x (cons (peek sorted-coll) acc))))))
(defn bubble-sort [coll]
"Insert x into a sorted collection"
(reduce insert-x [] coll))
(bubble-sort x)
;; => [2 4 5 7 8 9 10 12 23 30 54 55]
该代码完成了应做的工作。
但是,insert-x
不是那么优雅。如何以insert-x
对所有馆藏均有效的方式进行书写?这样更简单/更优雅?向量应返回向量,列表应返回列表等。
我想你想得太多了。
你有两个任务:
首先,我将这样重写insert-x
:
(defn insert-x [sorted-coll x]
(let [[l r] (split-with #(<= % x) sorted-coll)]
`(~@l ~x ~@r)))
请注意,它或多或少与你的变体相同:将值取到所需位置,然后将左右部分串联在一起x
。还要注意,它总是产生正确排序的列表,而与输入类型无关。
user> (insert-x [1 3 5 7 9] 10)
;;=> (1 3 5 7 9 10)
user> (insert-x [1 3 5 7 9] 0)
;;=> (0 1 3 5 7 9)
user> (insert-x [1 3 5 7 9] 4)
;;=> (1 3 4 5 7 9)
因此,你接下来需要做的就是减少输入并返回正确键入的结果:
(defn my-sort [coll]
(let [sorted (reduce insert-x () coll)]
(if (vector? coll)
(vec sorted)
sorted)))
user> (my-sort '(0 3 1 4 2 5 10 7))
;;=> (0 1 2 3 4 5 7 10)
user> (my-sort [0 3 1 4 2 5 10 7])
;;=> [0 1 2 3 4 5 7 10]
user> (my-sort ())
;;=> ()
user> (my-sort [])
;;=> []
(my-sort (shuffle (range 1000)))
由于lazyconcat
和lazy的堆叠,这可能会导致中等大小的集合的堆叠溢出split-with
。@ A.Webb。当然可以,但是既然这似乎是仅教育的任务(谁会在生产中真正使用它?)。否则
(doall `(~@l ~x ~@r))
解决此问题。@leetwinski您将如何在生产中使用它?(内置的排序功能?)
@ Gwang-JinKim它实际上取决于您的任务和输入大小。通常,内置
sort
就足够了,因此,如果有必要,您应该考虑对其进行优化。您还可以查看排序集,以防万一您需要将更改的数据保持排序状态,这比每次更改数据时都采用它更好。但是,除非有任何任务特定的信息,否则我们无法对此进行推理