有大量关于数据结构的文本和数据结构代码库。我知道纯粹的功能数据结构更容易推理。但是,我很难理解在实用代码中使用纯函数式数据结构(使用函数式编程语言与否)的命令对应的真实世界优势。有人可以提供一些真实世界的案例,其中纯粹的功能数据结构有优势吗?为什么?
我在 programming_language 中使用 data_structure_name 来执行应用,因为它可以执行 certain_thing 。
感谢。
PS:纯粹功能数据结构的含义与持久数据结构不同。持久性数据结构是一种不会改变的数据结构。另一方面,纯功能数据结构是纯粹运行的数据结构。答案 0 :(得分:71)
纯功能(也称为持久性或不可变)数据结构为您提供了几个优势:
cons
(值对和引用到下一个元素)并将其连接到上一个列表。在Java中,您必须创建全新的列表,以免损坏前一个列表。一般而言,它还有更多优点,它是对现实世界进行建模的另一种方式。来自SICP的This以及其他一些章节将为您提供更准确的不可变结构编程视图,优缺点。
答案 1 :(得分:24)
除了共享内存安全性之外,大多数纯函数数据结构还为您提供持久性,并且实际上是免费的。例如,假设我在OCaml中有一个set
,我想为它添加一些新值,我可以这样做:
module CharSet = Set.Make(Char)
let a = List.fold_right CharSet.add ['a';'b';'c';'d'] CharSet.empty in
let b = List.fold_right CharSet.add ['e';'f';'g';'h'] a in
...
添加新字符后 a
仍然未修改(它只包含广告),而b
包含啊,并且它们共享一些相同的内存(带有{ {1}}告诉共享多少内存是很棘手的,因为它是一个AVL树,并且树的形状发生了变化)。我可以继续这样做,跟踪我对树所做的所有更改,让我回到以前的状态。
这是一张来自Wikipedia article on Purely Functional的精彩图表,其中显示了将字符“e”插入二叉树set
的结果:
答案 2 :(得分:14)
Erlang程序几乎完全使用纯功能数据结构,并且通过几乎无缝扩展到多个内核,它们获得了巨大的好处。由于永远不会修改共享数据(主要是二进制文件和位字符串),因此无需锁定此类数据。
答案 3 :(得分:9)
拿这个F#的小片段:
let numbers = [1; 2; 3; 4; 5]
您可以100%确定地说这是整数1到5的不可变列表。您可以传递对该列表的引用,并且永远不必担心列表可能已被修改。这足以让我使用它。
答案 4 :(得分:8)
纯功能数据结构具有以下优点:
持久性:旧版本可以安全地重复使用,因为它们无法更改。
共享:只需要适度的内存要求,就可以同时保留多个版本的数据结构。
线程安全:任何突变都隐藏在懒惰的thunk中(如果有的话),因此由语言实现处理。
简单性:不必跟踪状态变化,使纯粹的功能数据结构更易于使用,特别是在并发环境中。
增量:纯功能数据结构由许多小部件组成,使其成为增量垃圾收集的理想选择,从而降低延迟。
请注意,我没有将并行性列为纯功能数据结构的优势,因为我不相信这种情况。高效的多核并行性需要可预测的局部性,以便利用高速缓存并避免在对主存储器的共享访问上遇到瓶颈,并且纯功能数据结构在这方面充其量具有未知特性。因此,许多使用纯功能数据结构的程序在多核上并行化时不能很好地扩展,因为它们将所有时间都花在缓存未命中,争夺共享内存路径。
纯粹功能数据结构的含义与持久数据结构不同。
这里有一些混乱。在纯功能数据结构的上下文中,持久性是一个术语,用于指代在知道它们仍然有效的情况下引用回安全数据结构的先前版本的能力。这是纯功能的自然结果,因此,持久性是所有纯功能数据结构的固有特征。