I'd like to @jozefg's answer, that it is possible to express the whole thing using folds. Any recursive operation on lists can be eventually expressed using foldr
, but often it's quite ugly.
First let's implement locMax
using mapMaybe
and zip
, very similarly to what @jozefg did::
import Prelude hiding (zip)
isMax :: (Ord a) => ((a, a), a) -> Maybe a
isMax ((x, y), z) | y > z && y > x = Just y
| otherwise = Nothing
locMax :: (Ord a) => [a] -> [a]
locMax xs@(_:xs'@(_:xs'')) = mapMaybe isMax $ zip (zip xs xs') xs''
Implementing mapMaybe
using foldr
isn't so difficult:
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
mapMaybe f = foldr (\x xs -> maybe xs (: xs) (f x)) []
Implementing zip
is a bit more tricky, since we need to consume two lists at once. What we'll do is that we'll accumulate inside foldr
a function of type [b] -> [(a,b)]
that'll consume the second list.
The base case is simple. If the first list is empty, the constructed function is const []
, so whatever is the second list, the result is also empty.
The folding step takes a value x : a
, an accumulated function that converts a sub-list of [b]
. And since we're producing a function again, we just take a third argument of type [b]
. If there are no b
s, the result is an empty list. If there is at least one, we construct a pair and call f
on the rest of b
s:
zip :: [a] -> [b] -> [(a,b)]
zip = foldr step (const [])
where
step _ _ [] = []
step x f (y : ys) = (x, y) : f ys
You can verify that this implementation has the required properties and works properly also if one or both lists are infinite.