Сопряжение смежных элементов списка в Haskell - PullRequest
11 голосов
/ 22 декабря 2010

У меня есть цепочка, как

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

И я хотел бы преобразовать его в список кортежей, используя соседние пары. Вот так

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

В данный момент я использую это, чтобы сделать это:

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

Однако мне не очень нравится этот метод. Кто-нибудь может придумать лучший способ? Если это очевидно, я прошу прощения, я довольно новичок в Хаскелле.

Ответы [ 3 ]

21 голосов
/ 22 декабря 2010

Хорошо, вот комментарий в качестве ответа:

Достаточно zipAdj x = zip x $ tail x.zip останавливается по достижении конца более короткого из двух списков, так что это просто объединяет каждый элемент в списке с его преемником, что, кажется, все, что вы хотите.

И для объяснениябессмысленная версия: zip <*> tail использует экземпляр Applicative для «функций из какого-либо типа», что в основном составляет легкую встроенную монаду Reader - в этом случае список является «средой» для Reader.Обычно это только запутывает вопросы, но в этом случае это почти проясняет ситуацию, если вы знаете, что вы читаете (<*>) здесь как «примените оба из них к одному аргументу, а затем примените первый ко второму».

8 голосов
/ 22 декабря 2010

Одно из возможных решений:

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

Определенно не такое маленькое, как ваше, и, вероятно, может быть оптимизировано совсем немного.

0 голосов
/ 17 сентября 2015

Можно обобщить 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)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...