是否有可能在Haskell中有一个双向链表,实现它们的理想解决方案是什么?我正在实现一个场景图,其中每个窗口小部件都有父窗口和子窗口,并且在图形中上下都是有益的。
答案 0 :(得分:39)
在Haskell中使用双向链表是不切实际的,因为你必须一次构建它。
例如,假设您有一个列表[1, 2, 3, 4, 5]
,您想要进行双重链接。现在,让我们想象一下如何表示列表:
data DoubleList a
= LeftEnd a (DoubleList a)
| Middle a (DoubleList a) (DoubleList a)
| RightEnd a (DoubleList a)
(为简单起见,我为两端使用了两个不同的构造函数)
要构建上面的列表,您必须首先构造第一个元素:
let e1 = LeftEnd 1 ...
但是要构造第一个元素,你需要有第二个元素:
let e1 = LeftEnd 1 e2
e2 = Middle 2 e1 ...
对于第二个元素,你需要第三个元素,等等:
let e1 = LeftEnd 1 e2
e2 = Middle 2 e1 e3
e3 = Middle 3 e2 e4
e4 = Middle 4 e3 e5
e5 = RightEnd 5 e4
由于懒惰的评估,这可以在Haskell中完成;这种策略被称为“打结”(并且你不必将它全部放在一个let
块中;你可以将结构划分为函数)
但是,换句话说,要创建一个双向链表,您需要一次构建它,如果您想要更改它的任何部分,您需要使用Zipper或每次都要制作一份完整的副本。
我建议改为使用Data.Sequence
,这是一种基于手指树的优化顺序存储实现。它支持非常快速的插入,删除和迭代,同时仍然是纯粹的功能数据结构。
否则,您可能只想使用Zippers,但将它们用于树而不是列表。有关Zippers的更多信息,请访问Haskell Wiki。拉链在这种情况下非常适合,因为它们提供了您所追求的确切功能:如果您访问带有拉链的树,您可以访问您正在访问的树部分的“父母”,但树本身不必包含父引用。
答案 1 :(得分:1)
由于你没有(通常)在Haskell中拥有OO风格的对象,所以认为数据是自我意识的是奇怪的。值得注意的是,您通常不会在Haskell数据类型中使用聚合,而是倾向于使用合成。
您可能需要查看XMonad以查看其设计是否符合您的需求(代码令人惊讶的可读性)。
您也可能希望重新构建您的设计,以便您永远不需要超越您(例如,通过传递子项“回调”)。
您可能还想知道是否可以为整个图形编写拉链。
答案 2 :(得分:0)
双向链接列表不是数据类型,而是实现细节。我想您想要的是一个类似列表的数据结构,您可以在其中高效地左右移动。此数据结构是列表的拉链,只是一对列表。前缀以相反的顺序表示。要左右移动,您只需将后缀列表的开头移到前缀上,反之亦然。