将列表中的(相邻)元素打包为2元组的方法

时间:2014-12-05 02:17:48

标签: haskell

我想知道是否会有一种简洁/单行的方式来执行以下操作:

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

与以下内容相同:

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

我对这两个选项中的任何一个都没有太多反对意见,但我想知道:将(,)与其他函数相结合是否有更简洁的方法?

我曾经假设有这样的方式,但它让我望而却步。所以这只是出于好奇。

谢谢!

4 个答案:

答案 0 :(得分:4)

我们可以轻松地将列表拆分为两个列表,其中包含带有此潮汐的交替元素(due to HaskellWiki

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

剩下的就是将列表与zip

组合在一起
pack :: [a] -> [(a, a)]
pack = uncurry zip . foldr (\a ~(x,y) -> (a:y,x)) ([],[])

答案 1 :(得分:2)

使用LambdaCaseData.List.unfoldr的另一个单行:

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

我偶尔想要的是splits -

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

鉴于此,pack变为:

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

答案 2 :(得分:1)

请注意,对于xs = [x1, x2, ..., xn-1, xn],我们有

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

导致

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

我们想要的是

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

一旦我们有了一个掩码列表就很容易获得

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

导致单线

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

答案 3 :(得分:0)

我不知道这是否是一行,但是:

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

应与您的功能相同。