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

前端 未结 4 1509
被撕碎了的回忆
被撕碎了的回忆 2021-01-23 02:57

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) = (         


        
相关标签:
4条回答
  • 2021-01-23 03:17

    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)) ([],[])
    
    0 讨论(0)
  • 2021-01-23 03:19

    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)
    
    0 讨论(0)
  • 2021-01-23 03:19

    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.

    0 讨论(0)
  • 2021-01-23 03:22

    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 ]
    
    0 讨论(0)
提交回复
热议问题