使用镜头插入特定位置的列表

时间:2013-08-28 03:52:21

标签: list haskell lenses

我正在尝试对包含元素列表的嵌套数据结构进行操作。在用各种方法解决之后,我终于决定使用镜头作为最好的方法。它们完美地用于查找和修改结构的特定元素,但到目前为止,我对如何添加新元素感到难过。

从我读过的内容来看,我无法在技术上使用Traversal,因为它违反了遍历法则将新元素插入到列表中,并且假设我甚至可以使用遍历中的遍历来弄清楚如何使用Traversal。第一名(我对Haskell来说仍然相当弱,并且镜头包装中大多数东西的类型签名让我头晕目眩)。

具体来说,我要完成的是,找到与特定选择器匹配的元素列表中的一些元素,然后在匹配元素之前或之后插入一个新元素(函数的不同参数之前为或者比赛结束后)。 Control.Lens是否已经拥有可以完成我正在尝试做的事情,而且我对类型签名的理解太弱而无法看到它?有没有更好的方法来完成我想要做的事情?

如果我只是想在列表的开头或结尾添加一个新元素,那将是相当微不足道的,但是将它插入特定于中间的某个地方是困难的部分。在我编写的一些前镜头代码中,我使用折叠来完成我想要的东西,但它开始变得粗糙在结构的更深嵌套部分(EG折叠内部的折叠内部)所以我转向Control.Lens试图解开一些混乱。

3 个答案:

答案 0 :(得分:5)

使用lens pacakge

如果我们开始知道函数id可以像镜头一样使用:

import Control.Lens
> [1,2,3,4] ^. id
[1,2,3,4]

然后我们可以继续讨论如何修改列表:

> [1,2,3,4] & id %~ (99:)
[99,1,2,3,4]

以上允许在列表开头插入。要关注列表的后半部分,我们可以使用_tail中的Control.Lens.Cons module

> [1,2,3,4] ^. _tail
[2,3,4]
> [1,2,3,4] & _tail %~ (99:)
[1,99,2,3,4]

现在将其概括为第n个位置

> :{
let
_drop 0 = id
_drop n = _tail . _drop (n - 1)
:}
> [1,2,3,4] ^. _drop 1
[2,3,4]
> [1,2,3,4] & _drop 0 %~ (99:)
[99,1,2,3,4]
> [1,2,3,4] & _drop 1 %~ (99:)
[1,99,2,3,4]

使用Cons instance对所有类型进行概括的最后一步,我们可以使用cons<|

> [1,2,3,4] & _drop 1 %~ (99<|)
[1,99,2,3,4]
> import Data.Text
> :set -XOverloadedStrings
> ("h there"::Text) & _drop 1 %~ ('i'<|)
"hi there"

答案 1 :(得分:2)

我认为一个简单的方法就是解决问题:

  • 一个[a] -> SomeAddtionalData -> [a]的函数,它基本上负责使用某些特定数据将列表转换为另一个列表。您可以在此处添加/删除列表中的元素并获取新列表
  • 使用lense从一些嵌套数据结构中提取List,将该列表传递给上面定义的函数,使用lense在嵌套数据结构中设置返回列表。

您的最后一段是关于当您尝试使用像Lens这样的通用抽象做太多时会发生什么的指示。这些通用抽象适用于某些通用目的,其他所有内容都特定于您的问题,并且应该围绕普通的旧函数进行设计(至少在最初,在您的项目中,您可能会在代码库中找到一些可以使用抽象的一般模式)类型等。)。

答案 2 :(得分:2)

对您的问题的一些评论:

回答问题: 可能有办法做你想做的事。镜头库非常通用。没有什么是一种简单或明显的方法来实现它。我认为它会涉及partsOf combinator,但我不确定。

对镜头的评论: 镜头库非常酷,可以应用于大量的问题。我在学习图书馆时最初的诱惑是尝试将所有东西都装入镜头访问或变异。我发现最好使用镜头库来挖掘我的复杂数据结构,但是一旦我有一个简单的元素,最好使用我已经知道的更传统的功能技术,而不是拉伸镜头库超过它是有用的限制。

您没有要求的建议: 将元素插入列表中间是个坏主意。并非它无法完成但最终可能成为O(n ^ 2)操作。 (See also this StackOverflow answer.)邮编列表或其他functional data structure可能是个更好的主意。作为附带好处,这些结构中的一些可以成为At类的实例,允许使用部分透镜组合器进行插入和删除。