Local maxima of list using fold
You could, but itd be very ugly. Mostly because to find the local maximum, you'll need to know the previous and next elements.
The previous element is no problem, but a fold isn't supposed to know anything about the elements after it.
As a possible approach
import Data.List (zip3)
localMax xs = map proj2
. filter isLocalMax
$ zip3 xs (tail xs) (drop 2 xs)
where isLocalMax (x, y, z) = x < y && z < y
proj2 (_,x,_) = x
So we shift the list by one and two and zip it all together so [a, b, c, d, e]
becomes
[(a, b, c), (b, c, d), (c, d, e)]
then we just use map
and filter
to select the appropriate elements.
Do note though that map
and filter
could be smashed into one foldr
if you really wanted to.
I'll try to only use the fold, as you asked. What about something like this?
lmax (x:y:xs) = third $ foldl f (x,y,[]) xs
where
third (_, _, x) = x
f (x, y, ls) z = (y, z, if y > x && y > z then y:ls else ls)
The idea is that you pass a tuple in the fold instead of a list of results. The tuple (a triple) will contain the last two elements and the list of results. The function evaluates whether the second element of the triple is a local minimum w.r.t. the first element (its predecessor) and the current element passed by the fold (its successor).
ghci> lmax [1,3,2]
[3]
ghci> lmax [3,4,2,5,1,7,6,1,9]
[7,5,4]
ghci> lmax [1..10]
[]
ghci> lmax []
*** Exception: test.hs:(4,1)-(5,66): Non-exhaustive patterns in function lmax
Anyway, from this it should be easy to use whatever method you prefer in order to return an empty result list when the input list is too short.
Please note that, by using foldl
, every new result is appended at the top. Because of this, the list of results is reversed. You might want to reverse again lmax
's results if you want to have them in the same order as in the original list: lmax' = reverse . lmax
.
localMaxAsUnfold :: [Int] -> [Int]
localMaxAsUnfold = unfoldr work
where
work (x:y:z:rest)
| y > x && y > z = Just (y, y:z:rest)
| otherwise = work (y:z:rest) -- cheat and recurse internally
work _ = Nothing
localMaxAsFold :: [Int] -> [Int]
localMaxAsFold = foldr work [] . makeTriples
where
makeTriples :: [Int] -> [(Int, Int, Int)]
makeTriples vals = zip3 vals (tail vals) (drop 2 vals)
work (x,y,z) vals
| y > x && y > z = y : vals
| otherwise = vals