Consider the function reverseAndMinimum
(slightly modified from another answer nearby):
import Control.Monad.State.Strict
reverseAndMinimum' :: Ord a => [a] -> State a [a] -> State a [a]
reverseAndMinimum' [ ] res = res
reverseAndMinimum' (x:xs) res = do
smallestSoFar <- get
when (x < smallestSoFar) (put x)
reverseAndMinimum' xs ((x:) <$> res)
reverseAndMinimum :: Ord a => [a] -> ([a], a)
reverseAndMinimum [ ] = error "StateSort.reverseAndMinimum: This branch is unreachable."
reverseAndMinimum xs@(x:_) = runState (reverseAndMinimum' xs (return [ ])) x
It traverses the argument only once; however, it is about 30% slower than a naive function that does it two times:
reverseAndMinimum_naive :: Ord a => [a] -> ([a], a)
reverseAndMinimum_naive xs = (reverse xs, minimum xs)
It also consumes about 57% as much memory.
Here are the relevant extracts from a run with +RTS -s
:
reverseAndMinimum
176,672,280 bytes allocated in the heap
...
INIT time 0.000s ( 0.000s elapsed)
MUT time 0.064s ( 0.063s elapsed)
GC time 0.311s ( 0.311s elapsed)
EXIT time 0.005s ( 0.005s elapsed)
Total time 0.379s ( 0.380s elapsed)
%GC time 82.0% (82.0% elapsed)
reverseAndMinimum_naive
112,058,976 bytes allocated in the heap
...
INIT time 0.000s ( 0.000s elapsed)
MUT time 0.041s ( 0.040s elapsed)
GC time 0.245s ( 0.245s elapsed)
EXIT time 0.005s ( 0.005s elapsed)
Total time 0.291s ( 0.291s elapsed)
%GC time 84.2% (84.3% elapsed)
What's happening, how can I diagnose, is it possible to improve?
P.S. A handy main
for running tests:
main = do
top <- (read :: String -> Int) . (!! 0) <$> getArgs
val <- evaluate . force $ reverseAndMinimum (take top [top, top - 1.. 1 :: Int])
print $ (\x -> (last . fst $ x, snd x)) $ val