在具有不可变数据的语言中对双向链接或循环数据结构实施操作的解决方法


11

我想学习如何在Haskell中制作图并对其执行一些本地操作,但是问题不是特定于Haskell的,我们可以考虑使用双链表来代替图。

问题:用 主要支持和倡导不可变数据结构(Haskell,Clojure等)的语言,惯用或推荐的方式来实现双链表(或其他双链或循环数据结构)及其操作。 ?特别是,如何使用语言正式禁止的就地更新?

我很容易想到,如果对双向链接列表执行某些本地操作(例如,如果插入了一个项目),由于语言的惰性,可能不需要立即复制整个列表。但是,由于列表是双重链接的,因此如果在一个地方进行修改,则旧节点都不能在列表的新版本中使用,因此,迟早需要对它们进行标记,复制和垃圾收集。显然,如果仅使用列表的更新副本,则这些操作是多余的,但是它们会增加与列表大小成比例的“开销”。

这是否意味着对于此类任务,不可变数据根本不合适,并且对可变数据没有“本地”支持的功能性声明语言不如命令式语言那么好?还是有一些棘手的解决方法?

附言:我已经在互联网上找到了一些有关该主题的文章和演示,但是很难追踪它们,而我认为这个问题的答案不应该超过一个段落或者是一个图表...我的意思是,如果有对于此问题,没有“功能性”解决方案,答案可能是“使用C”。如果有一个,那么它有多复杂?


相关问题


相关报价

纯粹的函数式编程语言允许非常简洁地表达许多算法,但是在少数算法中,就地可更新状态似乎起着至关重要的作用。对于这些算法,缺乏可更新状态的纯功能语言似乎固有地效率低下([Ponder,McGeer and Ng,1988])。

-约翰·劳伯伯里(John Launchbury)和西蒙·佩顿·琼斯(Simon Peyton Jones),《懒惰的功能状态线程》(1994),还有约翰·劳伯伯里和西蒙·佩顿·琼斯,《哈斯克尔州》(1995)。这些论文ST在Haskell中介绍了Monadic类型构造函数。


4
推荐:冈崎
罗伯特·哈维

2
感谢您的参考。我发现了他的论文
Alexey

这篇论文看起来很有希望:David King和John Launchbury 在Haskell(1994)中提出的惰性深度优先搜索和线性图算法
Alexey

看起来数组的类似问题由实现类型的diffarray包解决DiffArray。纵观diffarray包,我看到91次的出现unsafePerformIO。看来我的问题的答案是“是的,不,具有不变数据的纯功能语言不适合实现通常依赖于就地更新的算法”。
Alexey

我的当前的解决方案(在Haskell)是使用一个字典(MapIntMap,或HashMap),其为存储和使节点包含链接的节点的ID的。 “计算机科学中的所有问题都可以通过另一层间接解决。”
Alexey'1

Answers:


6

可能还有其他有效的不可变数据结构适合您的特定任务,但不像双向链接列表那样通用(不幸的是由于其可变性,它容易出现并发修改错误)。如果您更狭窄地指定问题,那么可能会找到这样的结构。

对于(相对)不变结构的经济遍历,通常的答案是透镜。 这个想法是,您可以保留足够的信息来从其未修改的部分和当前已修改的部分重建一个修改的不可变结构,并在其上导航到相邻节点。

另一个有用的结构是拉链。(有趣的是,镜头拉链的类型签名是该结构的类型签名的一种数学派生形式。)

这里有一些链接。


1
根据需要,拉链也可能有用
jk。

为了更狭义地说明我的问题,假设我要编写一个图形重写系统,例如基于图形重写的Lambda演算评估程序。
Alexey

1
@Alexey:您是否熟悉Clean人在图形重写方面的工作?wiki.clean.cs.ru.nl/…–
乔治

1
@Alexey:不是我所知道的:Clean是Haskell的一个堂兄,它是自己开发的。它也具有不同的机制来处理副作用(AFAIK被称为唯一类型)。另一方面,开发人员在图形重写方面做了很多工作。因此,他们可能是同时了解图形重写和函数编程的最佳人选。
乔治

1
我同意,如果我想导航并在我当前所在的位置进行修改,则拉链似乎可以使用双向链表或树来解决问题,但是尚不清楚如果我要关注几个位置该怎么办同时,例如,在两个相距很远的地方交换两个元素。还不清楚是否可以将其与“圆形”结构一起使用。
Alexey

2

Haskell不会阻止使用可变数据结构。由于使用它们的代码部分最终必须返回IO操作(必须最终将其绑定到主函数返回的IO操作),因此不鼓励使用它们,并且使它们更难使用。如果您确实需要它们,就无法使用它们。

我建议调查使用软件事务存储的方法。除了提供一种实现可变结构的有效方法之外,它还为线程安全性提供了非常有用的保证。请参阅模块描述https://hackage.haskell.org/package/stm,并在维基概述https://wiki.haskell.org/Software_transactional_memory


谢谢,我将尝试学习STM。它看起来像有在Haskell更多的方法有可变性和状态(我绊倒MVarStateST),所以我需要找出他们之间的分歧和预期用途。
Alexey,2015年

@Alexey:关于STIMO的要点,应该在答案中提及它,因为它允许运行有状态的计算,然后丢弃状态并将结果提取为纯值。
乔治

@Giorgio,是否可以将Haskell's ST与STM 一起使用,以使其具有并发状态和一次性状态?
Alexey

还有一个术语建议:组合的主IO操作不是“ 由主函数返回 ”,而是分配给main变量。:)(main甚至都没有功能。)
Alexey

我理解您的观点,但是“可变”在大多数人的心中仍然具有简单的价值,而不是产生价值的过程,而且主因显然更倾向于后者而不是前者。您建议的更改在技术上显然是正确的,但有可能使不熟悉该主题的人感到困惑。
Jules
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.