Return a list, which contains a pair of elements, but only if the respective elements' sums are odd

Viewed 433

Implement the oddPairs :: [Int] -> [Int] -> [(Int, Int)] function that returns a list of pairs, but only if the parameters' lists' respective elements' sums are odd.

For example:

oddPairs [1,2,3] [2,2,2] == [(1,2),(3,2)]

oddPairs [1,3,5] [2,4,6] == zip [1,3,5] [2,4,6]

oddPairs [1,2,3] [1,2,3] == []

So far, I've tried

oddPairs (x:xs) (y:ys) | (x+y) `mod` 2 == 0 = []
                       | (x+y) `mod` 2 /= 0 = [(x, y)] ++ oddPairs (xs) (ys)

And on the first example, it returns only [(1,2)], on the second, it returns the correct values but with a Non-exhaustive patterns error.

3 Answers

In case the two items are even, you should not just return an empty list, but continue the recursion until at least one of the lists is exhausted, so:

oddPairs :: Integral a => [a] -> [a] -> [(a, a)]
oddPairs [] _ = []
oddPairs _ [] = []
oddPairs (x:xs) (y:ys)
    -- keep searching for new items ↓
    | (x+y) `mod` 2 == 0 = oddPairs xs ys
    | otherwise = (x, y) : oddPairs xs ys

Another way to look at the problem is that you want only the pairs that have an odd sum. This is a slight difference in emphasis that might lead to the following.

Use the zip function to combine each list into pairs. Then use filter to find the ones with odd sum.

oddPairs :: Integral a => [a] -> [a] -> [(a, a)]
oddPairs f s = filter oddPair (zip f s)
    where oddPair (l, r) = not $ even (l + r)

Quite straightforwardly with a list comprehension:

oddPairs :: [Int] -> [Int] -> [(Int, Int)]
oddPairs ms ns = [(m, n) | (m, n) <- zip ms ns, odd (m + n)]

Indeed:

> oddPairs [1, 2, 3] [2, 2, 2] == [(1, 2),(3, 2)]
True
> oddPairs [1, 3, 5] [2, 4, 6] == zip [1, 3, 5] [2, 4, 6]
True
> oddPairs [1, 2, 3] [1, 2, 3] == []
True
Related