Skip to content

Instantly share code, notes, and snippets.

@smee
Last active February 5, 2018 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smee/4c6e95d0904c09e23154215a3b6d4c72 to your computer and use it in GitHub Desktop.
Save smee/4c6e95d0904c09e23154215a3b6d4c72 to your computer and use it in GitHub Desktop.
inital draft of a multi comparator, bug in the macro expansion
(ns multicompare)
;;; FIXME the ~key-fn expansion does not resolve all the symbols in `key-fn`, so advanced compilation in clojurescript fails!
(defn- multi-compare* [a b [[key-fn order] & key-fn-order-pairs]]
(let [c (gensym "compare-result-")
k (gensym "key-fn-")
compare-clause `(compare ~@(if (#{:asc :ASC :ascending :ASCENDING} order)
`[(~k ~a) (~k ~b)]
`[(~k ~b) (~k ~a)]))]
(if (nil? key-fn-order-pairs)
`(let [~k ~key-fn]
~compare-clause)
`(let [~k ~key-fn
~c ~compare-clause]
(if (zero? ~c)
~(multi-compare* a b key-fn-order-pairs)
~c)))))
(defmacro multi-compare
"Composite comparator for sorting collections
for different key functions in different sort orders.
Example:
(macroexpand-1 `(multi-compare [:foo :ASC :bar :DESC count :ASC]))
;==>
(fn [arg1 arg2]
(let [compare-result
(compare (:foo arg1) (:foo arg2))]
(if (zero? compare-result)
(let [compare-result
(compare (:bar arg2) (:bar arg1))]
(if (zero? compare-result)
(compare (count arg1) (count arg2))
compare-result))
compare-result)))
"
[& key-fn-order-pairs]
(assert (every? #(= 2 (count %)) key-fn-order-pairs))
(assert (every? #{:ASC :DESC} (map second key-fn-order-pairs)))
(let [a (gensym "arg1-")
b (gensym "arg2-")]
`(fn [~a ~b]
~(multi-compare* a b key-fn-order-pairs))))
(comment
;; example
(sort (multi-compare [:date :ASC]
[count :DESC]
[#(get % :foo) :DESC])
[{:date 1 :foo 1 :a 1}
{:date 2 :foo 1 :a 1 :b 2}
{:date 2 :foo 1 :a 1 :b 2 :c 3}
{:date 1 :foo 2 :a 1}
{:date 6 :foo 2 :a 1 :b 2}])
;; problem: in the exansion `count` and `fn*` from the anonymous functions have no namespace!
(clojure.pprint/pprint
(macroexpand-1
'(multi-compare [:foo :ASC]
[:bar :DESC]
[count :ASC]
[#(get % "id") :DESC])))
;; output:
(clojure.core/fn
[arg1-169175 arg2-169176]
(clojure.core/let
[key-fn-169178
:foo
compare-result-169177
(clojure.core/compare
(key-fn-169178 arg1-169175)
(key-fn-169178 arg2-169176))]
(if
(clojure.core/zero? compare-result-169177)
(clojure.core/let
[key-fn-169180
:bar
compare-result-169179
(clojure.core/compare
(key-fn-169180 arg2-169176)
(key-fn-169180 arg1-169175))]
(if
(clojure.core/zero? compare-result-169179)
(clojure.core/let
[key-fn-169182
count
compare-result-169181
(clojure.core/compare
(key-fn-169182 arg1-169175)
(key-fn-169182 arg2-169176))]
(if
(clojure.core/zero? compare-result-169181)
(clojure.core/let
[key-fn-169184 (fn* [p1__169172#] (get p1__169172# "id"))]
(clojure.core/compare
(key-fn-169184 arg2-169176)
(key-fn-169184 arg1-169175)))
compare-result-169181))
compare-result-169179))
compare-result-169177)))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment