在 Haskell 中配对相邻列表项

发布于 2024-10-09 04:00:03 字数 337 浏览 4 评论 0原文

我有一个像这样的链表

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

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

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

目前,我正在使用这个来做到这一点:

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

但是,我不太喜欢这种方法。有人能想出更好的办法吗?如果这是显而易见的,我道歉,我对 Haskell 相当陌生。

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. Like so

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

At the moment, I'm using this to do it:

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

However, I don't really like this method. Can anyone think of a better way? If it's glaringly obvious I apologise, I'm fairly new to Haskell.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不如归去 2024-10-16 04:00:03

好的,这是作为答案的评论:

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

为了解释毫无意义的版本:zip <*>; tail 使用 Applicative 实例来表示“某种类型的函数”,这基本上相当于一个轻量级内联 Reader monad——在这种情况下,列表是 Reader 的“环境”。通常这只会混淆问题,但在这种情况下,它几乎使它变得更清晰,假设您知道在这里阅读 (<*>) 为“将这两个应用于单个参数,然后应用第一个到第二个”。

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".

输什么也不输骨气 2024-10-16 04:00:03

一种可能的解决方案:

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

绝对不像你的那么小,并且可能可以优化很多。

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.

凉风有信 2024-10-16 04:00:03

可以将问题中的 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)]

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)]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文