John Smith
John Smith

Reputation: 99

Consecutive pairs with recursion on haskell

I have this code:

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

An example of what this code does:

> pairs "hey"
[('h','e'),('e','y')]
> pairs [1..7]
[(1,2),(2,3),(3,4),(4,5),(5,6),(6,7)]

I was wondering how to do this in recursive ways. I tried this,

pairs (x:xs) = (x,head xs): pairs xs

but it failed with the following exception

> pairs [1..7]
[(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,*** Exception: Prelude.head: empty list

Upvotes: 2

Views: 162

Answers (2)

Enlico
Enlico

Reputation: 28470

The problem with your attempt is that it assumes xs to have at least one element or, in other words, the whole input (x:xs) to have at least two elements, so it fails when it reaches the end.

You can solve this by writing a special case for the one-element list before the "normal" case you've already written:

pairs :: [a] -> [(a,a)]
pairs [_] = []
pairs (x:xs) = (x,head xs):pairs xs

if you also need it to work on empty lists, then you need to keep also the pairs [] = [] case.¹

(¹) Honestly, when I wrote the answer, I had already forgotten you were trying to emulate zip xs (tail xs)², so I haven't included the case for [] input.

(²) By the way, you really don't need pairs [] = [] in your zip version of the function, because zip xs (tail xs) works well with the empty list too, thanks to laziness.

Upvotes: 5

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477210

You can work with a pattern that selects the first two elements and recurses on the tail:

pairs :: [a] -> [(a, a)]
pairs [] = []
pairs [_] = []
pairs (x:xs@(x2:_)) = (x, x2) : pairs xs

or with less "unpacking":

pairs :: [a] -> [(a, a)]
pairs [] = []
pairs (x:xs) = go xs x
    where go [] _ = []
          go (x2:xs) x = (x, x2) : go xs x2

Upvotes: 6

Related Questions