Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

predicate and a list search haskell

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]

like image 295
Rebecca McGowan Avatar asked Nov 29 '25 20:11

Rebecca McGowan


1 Answers

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

like image 173
Benjamin Hodgson Avatar answered Dec 01 '25 21:12

Benjamin Hodgson