Dato
Dato

Reputation: 454

Ways to pack (adjacent) elements of a list into 2-tuples

I was wondering if there would be a concise/one-liner way to do the following:

pack :: [a] -> [(a, a)]
pack []       = []
pack [_]      = []
pack (x:y:xs) = (x, y) : pack xs

Which is the same as:

pack' xs = [(x, y) | (x, y, i) <- zip3 xs (tail xs) [0..], even i]

I don’t have much against either of these two options, but I was wondering: is there more concise way by combining (,) with some other function?

I had assumed there’d be such a way, but it eludes me. So this is just out of curiosity.

Thanks!

Upvotes: 2

Views: 630

Answers (4)

rampion
rampion

Reputation: 89143

Another one-liner using LambdaCase and Data.List.unfoldr:

pack = unfoldr $ \case (x:y:zs) -> Just ((x,y),zs); _ -> Nothing

Something I want occasionally is splits -

splits :: Int -> [a] -> [[a]]
splits n = unfoldr $ \case [] -> Nothing ; xs -> Just $ splitAt n xs

And given that, pack becomes:

pack xs = [ (a,b) | [a,b] <- splits 2 xs ]

Upvotes: 2

Cactus
Cactus

Reputation: 27656

Note that for xs = [x1, x2, ..., xn-1, xn], we have

init xs = [x1, x2, ... , xn-1]
tail xs = [x2, x3, ... , xn  ]

leading to

zip (init xs) (tail xs) = [(x1, x2), (x2, x3), (x3, x4), ...]

and what we want is

pack xs                 = [(x1, x2),           (x3, x4), ...]

which is easy to get once we have a list of masks

cycle [True, False]     = [ True,    False,    True, ...    ]

leading to the one-liner

pack :: [a] -> [(a, a)]
pack xs = map snd . filter fst . zip (cycle [True, False]) $ zip (init xs) (tail xs)

Upvotes: 1

Cirdec
Cirdec

Reputation: 24166

We can easily split the list into two lists with alternating elements with this tidbit (due to HaskellWiki)

 foldr (\a ~(x,y) -> (a:y,x)) ([],[])

All that remains is to combine the lists with zip

pack :: [a] -> [(a, a)]
pack = uncurry zip . foldr (\a ~(x,y) -> (a:y,x)) ([],[])

Upvotes: 5

Jonathan Cast
Jonathan Cast

Reputation: 4635

I don't know if this is one line, but:

snd $ foldr (\ x (z, ps) -> maybe (Just x, ps) (\y -> (Nothing, (x, y) : ps) z) (Nothing, []) $ xs

should be the same as your function.

Upvotes: 0

Related Questions