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 reuse the span to split the sequence into two parts: the left half, which all satisfies the predicate, and then the right half, starting with the first element that does not satisfy it. If there is no right half, then we can simply return the left half untouched. Otherwise, we must:
- Save the first element of the right half as a candidate element, which will be included only if we can find a later element that does not satisfy the predicate
- Include the previous candidate element, if any, as a result.
This is significantly more efficient for large lists (and especially endless lists!) Than the approach used in your question with repeated calls to last and init . But if this is not important to you, then simply applying the corrections proposed in the answer of Daniel Wagner, you will get a function that will be easier for you to understand.
Change As suggested in the comments, you can fix one corner case (an endless list of elements that 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 generating output, for example filterLast (< 1) $ 10 : repeat -1 . But I think that for any implementation this is impossible, since you will never know whether to include 10 in the output list, since you will never find another element greater than 1.
source share