Answering a question in SO, I stumbled into this problem:
(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]
The code does what it should.
However, insert-x
is not so elegant.
How to write insert-x
in a way that it is valid for all collections?
So that it is simpler/more elegant?
vectors should return vectors, lists should return lists etc.
i guess you're overthinking it.
you have two tasks:
first of all, i would rewrite the insert-x
like this for example:
(defn insert-x [sorted-coll x]
(let [[l r] (split-with #(<= % x) sorted-coll)]
`(~@l ~x ~@r)))
notice, it does more or less the same that your variant does: taking values until the desired positions, and then concatenating left and right parts with x
between them. notice also, it always produces properly sorted list, independent from the input type.
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)
so, the next thing you need, is just to reduce input and return the properly typed result:
(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 [])
;;=> []
This is likely to cause a stack overflow for moderately sized collections,
(my-sort (shuffle (range 1000)))
, due to stacking of lazyconcat
and lazysplit-with
.@A.Webb . Sure, but since it seems to be the education-only task (who would really use this in production?) that will do. Otherwise
(doall `(~@l ~x ~@r))
solves this.@leetwinski how would you approach it in production? (built-in sort functions?)
@Gwang-JinKim it essentially depends on your task and input size. Generally, built-in
sort
is good enough, so you should consider optimizing it iff it is necessary. You could also take a look at sorted-sets, in in case you need to keep your changing data sorted, which is better then resorting it every time it changes. But again, we can't reason about it unless we have any task specific information