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

Reply via email to