predicate and a list search haskell

4.2k Views Asked by At

I am learning Haskell at the moment and have come to a bit of a standstill. I'm trying to write a function that takes a predicate p and a list xs and returns the list of those elements of xs which immediately follow an element which passes the predicate p. Here is what I have :

afterFilter :: (a -> Bool) -> [a] -> [a]

afterFilter x (y:ys) =

    if x y

        then (map head [ys])

    else

        afterFilter x (tail ys) 

test input : afterFilter (<0) [-4,7,-4,-8,3,-3,-6,0,-9,-1]

output : [7]

3

There are 3 best solutions below

1
On BEST ANSWER

The trick is to pull two elements out of the input list by pattern-matching two cons cells. If the first element passes the predicate, we stick the second on the output. But don't forget to stick the second element back on the input list when you make the recursive call.

afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter f [] = []   -- input list is empty
afterFilter f [x] = []  -- input list has only one element - no "next element" to return
afterFilter f (x:y:xs) =
    let ys = afterFilter f (y:xs)
    in (if f x then y:ys else rest)

However, a higher-level - and much more Haskellish - way to approach the problem would be to break it down into a pipeline of operations.

  1. Pair up each item in the list with the element that follows it using zip, so we have a list of (element, next) pairs.
  2. Use filter to drop the pairs for which element does not pass the predicate.
  3. Use map to extract the next part of each surviving pair.

So the code looks like this:

pairWithSuccessors :: [a] -> [(a, a)]
pairWithSuccessors xs = zip xs (tail xs)

afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter p xs =
    let withSuccessors = pairWithSuccessors xs (tail xs)
        filtered = filter (\(element, next) -> p element) withSuccessors
        filteredSuccessors = map (\(element, next) -> next) filtered
    in filteredSuccessors

Or, written in point-free style:

afterFilter p = map snd . filter (p . fst) . pairWithSuccessors

Functions built with the composition operator . are read right-to-left: first pairWithSuccessors, then filter (p . fst), then map snd over the result.

GHC is good at working with lists: when compiled with optimisations, both approaches should produce roughly the same machine code - that is, there's no performance cost to the high-level solution

0
On

Following what you did, there are some strange things with your code :

The map head [ys] is very odd, and causes your function to stop : At the first element matching the predicate, your function returns a list containing its immediate successor and stops there. You still need to process the rest of the list.

Also, following your definition of the problem, each item which is a successor of an item passing the predicate should be on the resulting array. I may be wrong, but what I understood is that afterFilter (<0) [-1, -1, 1] should return [-1, 1].

However, you're discarding one element you didn't check for by calling tail ys : You checked for y, but not for head ys.

Finally, by adding the edge cases, here is what you get :

afterFilter :: (a -> Bool) -> [a] -> [a]

afterFilter _ [] = []
afterFilter _ [_] = []
afterFilter x (y:ys@(z:zs)) =
    if x y
        then z : afterFilter x ys
    else
        afterFilter x ys
0
On

Try:

afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter p [] = []
afterFilter p [_] = []
afterFilter p (x1:x2:xs) 
  | p x1 = x2:rest
  | otherwise = rest
  where rest = afterFilter p (x2:xs)

Or

afterFilter' :: (a -> Bool) -> [a] -> [a]
afterFilter' p xs = map snd $ filter (\(x, _) -> p x) $ zip xs (tail xs)

Or

afterFilter'' :: (a -> Bool) -> [a] -> [a]
afterFilter'' p xs = [y | (x, y) <- zip xs (tail xs), p x]