Reputation: 3169
I keep finding myself wanting a function for this, and I always need to implement it myself, but I feel like there must be a built-in for it.
Basically, what I want is sort of halfway between maximum
and maximumBy
.
maximum
takes a list of Ord
values and returns the maximum. This is O(n).
maximum :: (Ord a) => [a] -> a
maximumBy
takes a list of non-Ord
values and a sort function that provides an ordering for them, and computes the maximum using that function. This is O(nlogn) (or whatever the complexity is for a sort).
maximumBy :: (a -> a -> Ordering) -> [a] -> a
I want a maximumBy
that takes a list of values of type a
and a function that returns Ord
values for a
values, and computes the maximum a
using the Ord
values. This would be O(n) because it only needs to keep track of the maximum as it goes along. This is what that signature would be:
maximumBy' :: (Ord b) => (a -> b) -> [a] -> a
And this is my rough implementation:
maximumBy' func list = foldl1' (\max i -> let (maxby, iby) = (func max, func i) in if iby > maxby then i else max) list
Is there such a function built-in, or perhaps an easy way to write this using existing built-ins?
Upvotes: 2
Views: 3882
Reputation: 120741
That function is defined the tip-lib
package as
maximumOn :: (Foldable f, Ord b) => (a -> b) -> f a -> b
The general idea has also been adopted in recent base
, with
sortOn :: Ord b => (a -> b) -> [a] -> [a]
Note that thanks to laziness, you can use
maximumOn :: Ord b => (a -> b) -> [a] -> b
maximumOn f = head . sortOn f
and get the same asymptotic performance as a manual implementation.
Upvotes: 4
Reputation: 92117
I think what you are looking for is easily expressed using Data.Ord.comparing
:
Instead of using your maximumBy'
function:
maximumBy' head ["abc", "def", "geh"]
you can build a comparator with comparing
and give that to the usual maximumBy
:
maximumBy (comparing head) ["abc", "def", "geh"]
And as Willem Van Onsem says in another answer, you are incorrect to be concerned about the cost of maximumBy
: it does not sort the list.
Upvotes: 5
Reputation: 477264
First of all you make a claim that is wrong:
maximumBy
takes a list of non-Ord values and a sort function that provides an ordering for them, and computes the maximum using that function. This is O(nlogn).
No sorting has to be done to calculate the maximum element. An ordering relation has to be reflexive, anti-symmetric and transitive, and therefore one can simply alsways hold the maximum thus far, and use the custom comparison to check whether the new item is greater.
Furthermore you can construct you maximumBy'
function as:
maximumBy' f = maximumBy (compare `on` f)
Upvotes: 5
Reputation: 1494
I don't know any built-in function that does what you want, but you can really simplify your implementation of maximumBy'
.
You could use on
for example:
import Data.Function (on)
maximumBy' :: Ord b => (a -> b) -> [a] -> a
maximumBy' f = maximumBy (compare `on` f)
In fact, this definition is so simple you could use it as-is, without defining maximumBy'
at all.
Upvotes: 1