Th3Dark0
Th3Dark0

Reputation: 132

Return the element just before the occurrence of another element in haskell

I want to write a code in Haskell, to return an element just before the occurrence of another element in a list. For ex:

eBefore 3 [1,2,3,4,5] should return 2

I am quiet new to haskell. The code that i've written up till now is :

eBefore :: Eq a => a -> [a] -> Maybe a
eBefore n [] = Nothing
eBefore n (x:xs) = if x == n then Just x else eBefore n xs

I would be highly obliged if some one could help me understand the approach or help me out with the problem. Thank you!

Upvotes: 0

Views: 392

Answers (1)

ach
ach

Reputation: 2373

You can match more elaborated patterns:

eBefore n [] = Nothing
eBefore n [_] = Nothing
eBefore n (x1:xs@(x2:_))
    | x2 == n   = Just x1
    | otherwise = eBefore n xs

Here we return Nothing for lists containing zero or one elements because they contain no member with another one preceding them. (x1:xs@(x2:_)) is a pattern that matches a x1:xs, where xs in turn matches x2:_, that is, a list with at least two elements, the first element is bound to x1, the second to x2, the residue is unimportant (matched by _).

We also might write thus:

eBefore n [] = Nothing
eBefore n [_] = Nothing
eBefore n (x1:x2:xs)
    | x2 == n   = Just x1
    | otherwise = eBefore n (x2:xs)

However, this variant might be worse in terms of performance. (x1:x2:xs) is equivalent to (x1:(x2:xs)), and we see that (x2:xs) repeated again as an argument to recursive call. But the compiler may fail to recognize the identity of the two expressions and create a new node. That's a waste. By using the @-notation in the former variant, we give that (x2:_) from the pattern a name, xs, and pass it to the recursive call as a ready whole.

The difficult moment here is what we should return in case n is equal to the head of the list, e. g. eBefore 3 [3,4,5,6,3]. The definition above will skip the first occurrence of 3 and return 6.

Upvotes: 2

Related Questions