To quote Benjamin Stewart:

;; the body of this fn should probably be a macro that takes
;; any number of comparisons and or-chain them correctly such that
;; ties cascade to the next comparison and obviates the need for
;; explicit calls to false-if-zero.  Does it already exist?

This could be done with a macro, but I think that it could be done
simply with closure.  Here's my attempt.  I'm going to define two
helper functions first

(defn <=>
  "This creates a comparator that wraps the mapping fn f."
  [f]
  (fn [a b]
    (compare (f a) (f b))))

(defn inv-<=>
  "This creates an inverse comparator that wraps the mapping fn f."
  [f]
  (fn [a b]
    (compare (f b) (f a))))

These functions take in a mapping function and return a comparator/
inverse comparator, respectively.  Now, I'll define a chaining
function.

(defn chain-comp
  "This takes a list of comparator functions, and
  chains them together.  It returns another comparator.
  It behaves similar to comp, in that fns are applied
  right to left."
  [& comps]
  (fn [a b]
    (loop [remaining-comps (reverse comps)]
      (let [iter-comp (first remaining-comps)
            iter-result (iter-comp a b)]
        (if (and (zero? iter-result) (next remaining-comps))
          (recur (rest remaining-comps))
          iter-result)))))

Now, to define some simple test data

(def test-tuples
  [{:a 0 :b 1}
   {:a 0 :b 3}
   {:a 0 :b 2}
   {:a 1 :b 2}])

Since the chain comp returns a comparator, we'll use sort, and not
sort-by.

user=> (sort (chain-comp (<=> :b) (<=> :a)) test-tuples)
({:a 0, :b 1} {:a 0, :b 2} {:a 0, :b 3} {:a 1, :b 2})

user=> (sort (chain-comp (inv-<=> :b) (<=> :a)) test-tuples)
({:a 0, :b 3} {:a 0, :b 2} {:a 0, :b 1} {:a 1, :b 2})

I don't quite like the use of <=> and inv-<=>, but it the best
solution I could come up with that:

* Works with a closure (frequent readers should notice a theme in my
posts...)
* Allows inverting a comparison
* Uses the minimum amount of comparisons

Thoughts & feedback welcome

Sean

Gist available here:
http://gist.github.com/144590
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to