Haskell - Filter Last Element

后端 未结 4 1440
北荒
北荒 2021-01-18 17:36

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 
         


        
4条回答
  •  有刺的猬
    2021-01-18 18:17

    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:

    1. Save off the first element of the right half as a "candidate" item that will only be included if we can find a later element that doesn't satisfy the predicate
    2. Include the previous candidate element, if any, in the result.

    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.

提交回复
热议问题