Pairing adjacent list items in Haskell

前端 未结 3 622
情话喂你
情话喂你 2020-12-16 00:55

I have a chained list like

[\"root\", \"foo\", \"bar\", \"blah\"]

And I\'d like to convert it to a list of tuples, using adjacent pairs. Li

相关标签:
3条回答
  • 2020-12-16 01:32

    One possible solution:

    pairs [] = []
    pairs (x:[]) = []
    pairs (x:y:zs) = (x, y) : pairs (y : zs)
    

    Definitely not as small as yours, and can probably be optimized quite a bit.

    0 讨论(0)
  • 2020-12-16 01:34

    Okay, here's the comment as an answer:

    Just zipAdj x = zip x $ tail x will suffice. zip stops upon reaching the end of the shorter of the two lists, so this simply pairs each item in the list with its successor, which seems to be all you want.

    And for the sake of explaining the pointless version: zip <*> tail uses the Applicative instance for "functions from some type", which basically amounts to a lightweight inline Reader monad--in this case the list is the "environment" for the Reader. Usually this just obfuscates matters but in this case it almost makes it clearer, assuming you know to read (<*>) here as "apply both of these to a single argument, then apply the first to the second".

    0 讨论(0)
  • 2020-12-16 01:44

    It's possible to generalize the zipAdj in the question to work with arbitrary Traversable containers. Here's how we'd do it if we wanted the extra element on the front end:

    import Data.Traversable
    
    pairDown :: Traversable t => a -> t a -> t (a, a)
    pairDown x = snd . mapAccumL (\old new -> (new, (old,new))) x
    
    *Pairing> take 10 $ pairDown 0 [1..]
    [(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)]
    *Pairing> pairDown 0 [1..10]
    [(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)]
    

    To stick the extra element on the end, we can use mapAccumR:

    import Data.Traversable
    
    pairUp :: Traversable t => t a -> a -> t (a, a)
    pairUp xs x = snd $ mapAccumR (\old new -> (new, (new,old))) x xs
    

    This effectively traverses the container backwards.

    *Pairing> pairUp [0..10] 11
    [(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10),(10,11)]
    *Pairing> take 10 $ pairUp [0..] undefined
    [(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)]
    

    It's impossible to generalize the apparently-desired function in quite this fashion, but it's possible to generalize it a bit differently:

    import Data.Foldable
    import Prelude hiding (foldr)
    
    pairAcross :: Foldable f => f a -> [(a,a)]
    pairAcross xs = foldr go (const []) xs Nothing
      where
        go next r Nothing = r (Just next)
        go next r (Just prev) = (prev, next) : r (Just next)
    

    This gives

    *Pairing> pairAcross [1..10]
    [(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)]
    
    0 讨论(0)
提交回复
热议问题