GlossMash
GlossMash

Reputation: 89

Find the preceding element of an element in list (Haskell)

I want to return the preceding element of an element in list. I am intending to get the index of parameter and use it to deprecate list such that parameter is the last element, then reverse it and take the second element of reversed list. I get error: type elemIndex is Maybe Int while take function require Int. I want to fix it or write code using simple recursion Are there a shorter code using recursion?

precedingElement :: Eq a => a -> [a] -> Maybe a
precedingElement elt lst    | lst == [] = error "List is empty"
                            | elt `notElem` lst = Nothing
                            | otherwise = Just x where x = snd (reverse (take (elt `elemIndex` lst) lst))

Upvotes: 2

Views: 1214

Answers (4)

user6722421
user6722421

Reputation: 11

Sort-of standard solution for this is to use zipping:

import Data.List (find)

preceding :: (a -> Bool) -> [a] -> Maybe a
preceding f xs = fmap snd . find (f . fst) $ zip (drop 1 xs) xs

Upvotes: 1

pigworker
pigworker

Reputation: 43383

One of my favourite underappreciated utilities is rather handy for problems like this. Let me have backward lists, so I don't need to reverse my brain.

data Bwd x = B0 | Bwd x :< x  -- rightmost is nearest

Think of list elements as the beads on an abacus wire. Flick a few to the left and leave your finger resting on the next one. What have you? A list of beads to the left of your finger (with the rightmost nearest), a list of beads to the right of your finger (with the leftmost nearest), and the bead with your finger on it.

That is, a one-hole element context for lists is given by the pair of backward and forward lists either side of the hole.

type ListContext x = (Bwd x, [x])

Those who know my old songs recognize ListContext as the derivative of [].

An element in focus (your finger on a bead) is

type ListFocus x = (ListContext x, x)

And there is a useful operation which decorates every list element with its context, putting it in focus.

focus :: [x] -> [ListFocus x]
focus = go B0 where
  go xz [] = []
  go xz (x : xs) = ((xz, xs), x) : go (xz :< x) xs

For example,

focus [1,2,3] = [((B0,[2,3]),1), ((B0 :< 1,[3]),2), ((B0 :< 1 :< 2,[]),3)]

and now it is very easy to answer all sorts of questions that concern an element and its immediate surroundings. You mightn't construct focus just to solve this problem, but it's the sort of thing I keep around because it solves lots of problems.

[p | ((_ :< p,_),q) <- focus xs, q == x]

computes all the values p which sit to the left of an x in xs. As you can see.

(By the way, this focus operation didn't come from nowhere. It arises from the differential structure of the datatype of lists. This answer (where it is called picks) tells the list story in more detail, and this answer develops the datatype generic story.)

Upvotes: 9

dfeuer
dfeuer

Reputation: 48591

As I'm a big fold fan, I might write something like

lastBefore :: (a -> Bool) -> [a] -> Maybe a
lastBefore p xs = foldr go (`seq` Nothing) xs Nothing
  where
    go x r (Just prev) | p x = Just prev
    go x r _ = r (Just x)

But I'd have to check that GHC optimizes it as I wish.

Upvotes: 2

Code-Apprentice
Code-Apprentice

Reputation: 83527

In order to return the preceding element of the given element, you can use some pattern matching and recursion:

precedingElement _ [] = Nothing
precedingElement _ [x] = Nothing
precedingElement elt (x:y:rest)
    | y == elt = Just x
    | otherwise = precedingElement elt (y:rest)

Upvotes: 5

Related Questions