Ugh. And if I were slightly lazier at pressing the send button, I would
have realized that the laziness of map and remove gives this short-circuit
evaluation.
I generalized your example to allow the notation [- keyfn] as an argument
to specify descending order on that key, instead of ascending order. It
doesn't check its arguments carefully, but if you give it correct
arguments, it works :-)
(defn multicmp [& keys]
(fn [a b]
(or (first (remove zero? (map #(if (vector? %)
(let [[order keyfn] %]
(if (= order -)
(compare (keyfn b) (keyfn a))
(compare (keyfn a) (keyfn b))))
(compare (% a) (% b)))
keys)))
0)))
Example, with implicit ascending order for :name, but explicit ascending
order for :salary:
(pprint (sort (multicmp :name [- :age] [+ :salary]) employees))
Thanks,
Andy
On Wed, Dec 7, 2011 at 7:23 PM, Andy Fingerhut <[email protected]>wrote:
> The intent of making it a macro is that it allows short-circuit
> evaluation, like Clojure's or/and. There is no reason to evaluate
> comparisons between keys when an earlier comparison has already decided the
> ordering.
>
> Hardwiring in the compare function and the order between %1 and %2 is an
> interesting idea I hadn't considered, partly because it is often the case
> that one would like an ascending sort order on one key, but descending on
> others, which is commonly done by swapping %1 and %2's position, but could
> also be done by negating the return value of compare.
>
> Andy
>
>
> On Wed, Dec 7, 2011 at 6:59 PM, Alan Malloy <[email protected]> wrote:
>
>> This should be a function, not a macro. In fact it is just:
>>
>> (defn multicmp [& xs] (first (remove zero? xs)))
>>
>> But what you really wanted to begin with is a comparator function, so
>> more like:
>>
>> (defn multicmp [& keys]
>> (fn [a b]
>> (or (first (remove zero? (map #(compare (% a) (% b))
>> keys)))
>> 0)))
>>
>> (sort-by (multicmp < =) coll)
>>
>> On Dec 7, 5:51 pm, Andy Fingerhut <[email protected]> wrote:
>> > I've been going through the PLEAC web site, writing Clojure examples
>> > corresponding to the Perl code examples from the Perl Cookbook:
>> >
>> > http://pleac.sourceforge.net
>> >
>> > Michael Bacarella started a github repo to collect these together, and
>> I'm
>> > helping flesh some of them out.
>> >
>> > https://github.com/mbacarella/pleac-clojure
>> >
>> > One thing that is very convenient in Perl is doing sorts on multiple
>> > comparison keys -- since 0 is treated as logically false in Perl, they
>> can
>> > simply do what in Clojure would look like:
>> >
>> > (sort #(or (compare (key1 %1) (key2 %2))
>> > (compare (key2 %1) (key2 %2))
>> > ...)
>> > collection)
>> >
>> > This doesn't work in Clojure, because it only treats nil or false as
>> > logically false. It is easy to write a short-circuiting macro
>> 'multicmp'
>> > that does the correct thing:
>> >
>> > (defmacro multicmp
>> > ([x] x)
>> > ([x & next]
>> > `(let [cmp# ~x]
>> > (if (not= cmp# 0)
>> > cmp#
>> > (multicmp ~@next)))))
>> >
>> > Then just replace 'or' in the first code snippet with 'multicmp'.
>> >
>> > Does something like this already exist in a library I'm unaware of?
>> Would
>> > others find it useful to have around? And any suggestions on where it
>> best
>> > belongs if so?
>> >
>> > Thanks,
>> > Andy
>>
>> --
>> 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
>
>
>
--
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