如何实现双向链表

时间:2012-04-30 15:52:23

标签: haskell doubly-linked-list

是否有可能在Haskell中有一个双向链表,实现它们的理想解决方案是什么?我正在实现一个场景图,其中每个窗口小部件都有父窗口和子窗口,并且在图形中上下都是有益的。

3 个答案:

答案 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)

双向链接列表不是数据类型,而是实现细节。我想您想要的是一个类似列表的数据结构,您可以在其中高效地左右移动。此数据结构是列表的拉链,只是一对列表。前缀以相反的顺序表示。要左右移动,您只需将后缀列表的开头移到前缀上,反之亦然。