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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---