配对Haskell中的相邻列表项

时间:2010-12-22 03:11:07

标签: list haskell zip

我有像

这样的链式列表
["root", "foo", "bar", "blah"]

我想使用相邻的对将其转换为元组列表。像这样

[("root", "foo"), ("foo", "bar"), ("bar", "blah")]

目前,我正在用它来做这件事:

 zipAdj x = tail (zip ("":x) (x++[""]))

但是,我真的不喜欢这种方法。谁能想到更好的方法?如果我很明显地道歉,我对Haskell来说还是新手。

3 个答案:

答案 0 :(得分:21)

好的,这是评论的答案:

只需zipAdj x = zip x $ tail x即可。 zip在到达两个列表中较短列表的末尾时停止,因此这只是将列表中的每个项目与其后继项目配对,这似乎就是您想要的。

为了解释无意义的版本:zip <*> tail使用Applicative实例用于“某种类型的函数”,这基本上相当于一个轻量级的内联Reader monad - 在这种情况下是列表是读者的“环境”。通常这只是模糊了事情,但在这种情况下,它几乎使它更清晰,假设你知道在这里阅读(<*>)“将这两个应用于一个参数,然后将第一个应用于第二个”。

答案 1 :(得分:8)

一种可能的解决方案:

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

绝对不会像你的那么小,并且可能会有相当的优化。

答案 2 :(得分:0)

可以概括问题中的zipAdj以使用任意Traversable个容器。如果我们想要前端的额外元素,我们就会这样做:

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)]

要将额外元素添加到最后,我们可以使用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

这有效地遍历容器向后

*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)]

以这种方式推广显然所需的功能是不可能的,但是可以稍微区别一点:

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)

这给出了

*Pairing> pairAcross [1..10]
[(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)]