This function returns a 2-tuple with the minimum and the maximum of a list:
import Control.Arrow ((***), (>>>), (&&&))
import Data.Semigroup (getMin, getMax, Min(..), Max(..))
bounds :: (Bounded a, Ord a) => [a] -> (a,a)
bounds = foldMap (Min &&& Max) >>> getMin *** getMax
Example:
> x = [1..10 :: Int]
> bounds x
(1,10)
Is it more efficient than (minimum x, maximum x)
?
Or is there another way more efficient than (minimum x, maximum x)
?
First off, your two functions do not behave the same. (minimum xs, maximum xs)
dies when xs
is an empty list.
Is it more efficient than
(minimum x, maximum x)
?
They're both O(n), but the only way to answer questions like this is to benchmark them competitively. I think I'd expect the foldMap
solution to be faster, as it makes only a single pass over the list, but let's find out.
import Control.Arrow ((***), (>>>), (&&&))
import Data.Semigroup (getMin, getMax, Min(..), Max(..))
import System.Random
import Criterion.Main
bounds1, bounds2 :: (Bounded a, Ord a) => [a] -> (a,a)
bounds1 = foldMap (Min &&& Max) >>> getMin *** getMax
bounds2 xs = (minimum xs, maximum xs)
randomList :: Int -> IO [Int]
randomList count = take count <$> randoms <$> newStdGen
mkBench n = env (randomList n) $ \list -> bgroup (show n) [
bench "foldMap" $ nf bounds1 list,
bench "minMax" $ nf bounds2 list
]
main = defaultMain $ map mkBench [100, 1000, 10000, 100000, 1000000]
Tabulated, that's
100/foldMap 1.411 μs
100/minMax 517.6 ns
1000/foldMap 28.94 μs
1000/minMax 5.078 μs
10000/foldMap 488.4 μs
10000/minMax 51.56 μs
100000/foldMap 21.08 ms
100000/minMax 537.3 μs
1000000/foldMap 268.9 ms
1000000/minMax 8.989 ms
So (minimum xs, maximum xs)
turns out to be faster than the foldMap
idea, across the board.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With