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 -~----------~----~----~----~------~----~------~--~---