我有像
这样的链式列表["root", "foo", "bar", "blah"]
我想使用相邻的对将其转换为元组列表。像这样
[("root", "foo"), ("foo", "bar"), ("bar", "blah")]
目前,我正在用它来做这件事:
zipAdj x = tail (zip ("":x) (x++[""]))
但是,我真的不喜欢这种方法。谁能想到更好的方法?如果我很明显地道歉,我对Haskell来说还是新手。
答案 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)]