I want to filter the last element of a list that does not satisfy a property. An example would be
smallerOne :: a->Bool
smallerOne x = x < 1
Here is one possible implementation of filterLast
:
filterLast :: (a -> Bool) -> [a] -> [a]
filterLast p = go []
where go chunk xs = case span p xs of
(left, []) -> left
(left, (r:right)) -> chunk ++ left ++ go [r] right
The idea is to repeatedly use span
to split the sequence into two parts: a left half that all satisfy the predicate, and then a right half starting with the first element that doesn't satisfy it. If there is no right half, then we can just return the left half untouched. Otherwise, we have to:
This is substantially more efficient for large lists (and especially infinite lists!) than the approach used in your question, with repeated calls to last
and init
. But if this is not an important concern for you, then simply applying the fixes suggested in Daniel Wagner's answer will get you a function that you will find easier to understand.
Edit: As suggested in the comments, you can fix one corner case (an infinite list of items that all satisfy the predicate) by renaming this function to filterLast'
and then defining a new filterLast
that delegates to it:
filterLast :: (a -> Bool) -> [a] -> [a]
filterLast p xs = left ++ filterLast' p right
where (left, right) = span p xs
Note that there are still some sequences where this diverges without ever producing output, such as filterLast (< 1) $ 10 : repeat -1
. But I think it's impossible for any implementation to address that, because you never find out whether or not to include the 10
in the output list, since you never find another element greater than 1.