Search code examples
collectionsclojurebubble-sort

Better function for collections


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.


Solution

  • i guess you're overthinking it.

    you have two tasks:

    1. insert item at the proper position inside a sorted collection
    2. return vector for input vector and list for input list

    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 [])
    ;;=> []