On Sat, Mar 24, 2012 at 9:39 PM, Cedric Greevey <cgree...@gmail.com> wrote: > In increasing order of difficulty... > > Option 1: > > Extend your comparator to sort first on the key you're actually > interested in, then if that key isn't different on the others > more-or-less arbitrarily.
Or use this: (defn po->to [po] (let [m (atom #{})] (fn [a b] (or (po a b) (and (not (po b a)) (or (< (hash a) (hash b)) (and (== (hash a) (hash b)) (not= a b) (or (contains? @m [a b]) (and (not (contains? @m [b a])) (swap! m conj [a b])))))))))) (defn sorted-set-po [po & keys] (apply sorted-set-by (po->to po) keys)) Input: a partial order (may be true for a b, true for b a, or false for both). Output: a sorted-set that uses a total order built from the partial order. For things where the po says neither a < b nor b < a, it will first compare their hashes. If those don't collide, the order of a and b is decided that way. If they do, and a and b are not actually equal, it will arbitrarily pick one to be "less than" the other and remember its decision so that the ordering of unequal, hash-colliding items will remain consistent. Notes: 1. The memory use can grow over time due to the memoization-like behavior using the atom. However, this only occurs for items with both order and hash collisions, which should be extraordinarily infrequent. (If hashes were uniform on [0,2^32-1] you'd need an average of around 65,000 items before, per the birthday paradox, a single collision was likely within your collection. And then it would probably be for a pair of items that po considered unequal.) So the memory use growth should be small (for collections of thousands to millions of items) to nonexistent (for smaller ones). 2. Two sorted-set-pos with the same items, where they were added in different sequences, may not sort them in the same order, though this should be rare (again tied to the frequency of order+hash collisions). But this is not really a problem since, if you're sorting with a partial order, it makes sense to consider the ordering within equality buckets of the partial order to be undefined (the way ordering of an entire hash-set is undefined) and thus does not make sense to make program semantics depend on the ordering. Also, all sets compare equal purely by having the same contents (I checked APersistentSet.java to be sure) so the two sorted-set-pos would still compare equal despite traversing their elements in slightly different sequences. Here's a clooj REPL session testing po->to. Note that the last tests intentionally induce an order+hash collision of unequal items and check that the same instance of a po->to produces consistent results when given the colliding items in either order (that is, it always returns logical true when given the items in one order and always returns logical false when given them in the opposite order). The very last tests verify that it always returns false for equal items. user=> (def foo (po->to #(< (:a %1) (:a %2)))) #'user/foo user=> (foo {:a 1 :b 1} {:a 2 :b 1}) true user=> (foo {:a 1 :b 1} {:a 2 :b 1}) true user=> (foo {:a 1 :b 1} {:a 2 :b 1}) true user=> (foo {:a 2 :b 1} {:a 1 :b 1}) false user=> (foo {:a 2 :b 1} {:a 1 :b 1}) false user=> (foo {:a 1 :b 1} {:a 1 :b 2}) true user=> (foo {:a 1 :b 1} {:a 1 :b 2}) true user=> (foo {:a 1 :b 1} {:a 1 :b 2}) true user=> (foo {:a 1 :b 2} {:a 1 :b 1}) false user=> (foo {:a 1 :b 2} {:a 1 :b 1}) false user=> (foo {:a 1 :b 2} {:a 1 :b 1}) false user=> (hash {:a 1 :b 1 :c 1}) -1253235521 user=> (hash {:a 1 :b 1 :c 2}) -1253235522 user=> (hash {:a 1 :b 2 :c 1}) -1253235520 user=> (hash {:a 1 :b 0 :c 1}) -1253235522 user=> (foo {:a 1 :b 1 :c 2} {:a 1 :b 0 :c 1}) #{[{:a 1, :c 2, :b 1} {:a 1, :c 1, :b 0}]} user=> (foo {:a 1 :b 1 :c 2} {:a 1 :b 0 :c 1}) true user=> (foo {:a 1 :b 1 :c 2} {:a 1 :b 0 :c 1}) true user=> (foo {:a 1 :b 0 :c 1} {:a 1 :b 1 :c 2}) false user=> (foo {:a 1 :b 0 :c 1} {:a 1 :b 1 :c 2}) false user=> (foo {:a 1 :b 0 :c 1} {:a 1 :b 1 :c 2}) false user=> (def bar (po->to #(< (:a %1) (:a %2)))) #'user/bar user=> (bar {:a 1 :b 0 :c 1} {:a 1 :b 1 :c 2}) #{[{:a 1, :c 1, :b 0} {:a 1, :c 2, :b 1}]} user=> (bar {:a 1 :b 0 :c 1} {:a 1 :b 1 :c 2}) true user=> (bar {:a 1 :b 1 :c 2} {:a 1 :b 0 :c 1}) false user=> (bar {:a 1 :b 1 :c 2} {:a 1 :b 0 :c 1}) false user=> (bar {:a 1 :b 1 :c 2} {:a 1 :b 1 :c 2}) false user=> (bar {:a 1 :b 1 :c 2} {:a 1 :b 1 :c 2}) false user=> (bar {:a 1 :b 0 :c 1} {:a 1 :b 0 :c 1}) false user=> (bar {:a 1 :b 0 :c 1} {:a 1 :b 0 :c 1}) false -- 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