tags:

views:

164

answers:

5

I have the following two functions written.

pair :: [a] -> [(a, a)]
pair [] = []
pair [x] = []
pair (x1:x2:xs) = (x1, x2) : pair xs

unpair :: [(a, a)] -> [a]
unpair [] = []
unpair ((x1, x2):xs) = x1 : x2 : unpair xs

Pair will take pairs of elements and make 2-tuples of them. If the list has an odd number of elements, discard the last one. Unpair is the reverse of pair.

These work, but wondering whether there is a more succinct way to write these.

+4  A: 

One-liners:

pair xs = map snd . filter fst . zip (iterate not True) $ zip xs (drop 1 xs)
unpair = concatMap (\(x,y) -> [x,y])

You could have also abbreviate your definition of pair a little:

pair (x1:x2:xs) = (x1, x2) : pair xs
pair _ = []
jetxee
I like the abbreviation. Thanks.
qrest
+2  A: 

It's not any more concise, but for the sake of clarity I'd use splitEvery from Data.List.Split for pair:

pair = map tuplify . filter ((>1) . length) . splitEvery 2
  where
    tuplify [x, y] = (x, y)

This is off the top of my head—it would be nicer to check the length of the last list only.

For unpair I'd use foldr to avoid the explicit recursion:

unpair = foldr (\(x, y) -> (x:) . (y:)) []

This is just a matter of taste.

Travis Brown
+1 to support my ongoing campaign promoting use of the name `tuplify`.
camccann
`unpair = foldr (\\(x, y) -> ([x, y] ++)) []` is nicer imo.
trinithis
A: 
pair s = dropEven $ zip s (tail s)
     where dropEven s = map fst $ filter snd $ zip s (cycle [True, False])

unpair = concatMap (\(a, b) -> [a, b])

Though I definitely prefer your definition of pair.

rkhayrov
We came up with almost identical solutions :-)
jetxee
+2  A: 

So many possibilities. How about these?

unpair' = concatMap (\(x,y) -> [x,y])
pair' xs = map snd . filter fst . zip (cycle [True, False]) $ zip xs (tail xs)
pair'' xs = [(x,y) | (True,x,y) <- zip3 (cycle [True,False]) xs (tail xs)]

The two versions of pair should be the same.

Edit: Regarding my comment above, one can use the split package from Hackage to write:

pair xs = map head . splitEvery 2 $ zip xs (tail xs)

which is closer to the desired

pair xs = everyOther $ zip xs (tail xs)

But, in the spirit of pointlessness, I think we should probably all agree on writing it,

pair = map head . splitEvery 2 . (zip <$> id <*> tail)

to ensure confusion.

Anthony
Nice version with list comprehension. +1.
jetxee
A: 

This is a nice use for view patterns:

{-# LANGUAGE ViewPatterns #-}

pair :: [a] -> [(a,a)]
pair (splitAt 2 -> ([x,y],ys)) = (x,y) : pair ys
pair _ = []

unpair :: [(a,a)] -> [a]
unpair = (>>= \(x,y) -> [x,y])
Greg Bacon
I don't think that `pair (splitAt 2 -> ([x,y],ys)) =` is more readable than just `pair (x:y:ys) =`.
jetxee
But that doesn't use view patterns... `:-)`
Greg Bacon