Reputation: 454
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
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
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
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
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