How can I convert a list of 3-tuples into one 3-triple of lists in Haskell?

Viewed 169

I am attempting to write a function called that takes a list of triples and produces a 3-tuple of three lists. For instance, the function called on [(a,b,c), (d, e, f), (g, h, i)] should produce ([a,d,g], [b, e, j], [c, f, i]).

I have this so far:

-- This gives the head of a 3-tuple
fst3 :: (a, b, c) -> a
fst3 (x, _, _) = x

unzipTriples :: [(a, b, c)] -> a
unzipTriples (x : y : z : xs) = (fst3 x : fst3 y : fst3 z, unzipTriples xs)

I thought this would create a list [a, d, g] and then add it to the 3-tuple and do the rest for the remaining list. How can I improve this wrong function?

1 Answers

The pattern (x : y : z : xs) in unzipTriples (x : y : z : xs) = … will take the first three elements of the list, so then x, y and z are 3-tuples. But you thus would limit yourself to processing lists with three or more elements.

What you can do is use map and thus work with:

fst3 :: (a, b, c) -> a
fst3 (a, _, _) = a

snd3 :: (a, b, c) -> b
snd3 (_, b, _) = b

thd3 :: (a, b, c) -> c
thd3 (_, _, c) = c

unzipTriples :: [(a, b, c)] -> ([a], [b], [c])
unzipTriples xs = (map fst3 xs, map snd3 xs, map thd3 xs)

But it is probably more elegant, and will consume less memory when you pattern match on the first item of the list, a 3-tuple, and then recurse on the tail of the list, so:

unzipTriples :: [(a, b, c)] -> ([a], [b], [c])
unzipTriples [] = ([], [], [])
unzipTriples ((a, b, c):xs) = (a:as, b:bs, c:cs)
    where ~(as, bs, cs) = unzipTriples xs

You can convert this into an expression with foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b, I leave this as an exercise.

Related