Continuing my journey of using Udacity’s python-based CS212 and translating to Clojure, I am trying to write a function that:
- Returns all items in a sequence,
- that are equal to the maximum of the sequence,
- using a “key” function that returns a vector
Clojure’s max-key function does almost this, except that it requires my key to return an integer, which in this case I don’t–I return a vector. With some modification, I can use the compare function to compare the vectors returned by my keying function:
(defn all-max-key
"Returns a vector of x for which (k x), arbitrated by compare, are greatest."
([k x] x)
([k x y] (if (= 1 (compare (k x) (k y))) x y))
([k x y & more]
(reduce #(all-max-key k %1 %2) (all-max-key k x y) more)))
Now, the problem is that this doesn’t account for ties (where compare returns 0). In other words, all elements in my list are unique, but some may have equivalent values per my keying function. In imperative world, I might loop through the list, keeping track of my max value, comparing each element to it, and then appending / replacing a mutable list as I go.
But I feel like there has to be an idiomatic, elegant, functional way of doing this without resorting to loops. My attempts to use reduce have all resulted in nonsensical comparisons of sequences to member elements. Can anyone shine light on this?
This returns a vector of the maximal items:
Testing at the REPL:
Note that
comparemay return an arbitrary positive integer to indicate its first argument is greater than the second — hence thepos?in place of== 1.