有关数据结构的大量文本和数据结构代码库 . 我知道纯粹的功能数据结构更容易推理 . 但是,我很难理解在实用代码中使用纯函数数据结构(使用函数式编程语言与否)在命令式对应方面的真实优势 . 有人可以提供一些真实世界的案例,其中纯功能数据结构具有优势,为什么?
这一行的例子就像我在 programming_language 中使用 data_structure_name 做 application 因为它可以做 certain_thing .
谢谢 .
PS:我的意思是纯功能数据结构与持久数据结构不同 . 持久数据结构是一种不会改变的数据结构 . 另一方面,纯功能数据结构是纯粹运行的数据结构 .
5 回答
纯功能(也称为持久性或不可变)数据结构为您提供了以下几个优点:
你永远不必锁定它们,这极大地改善了 concurrency .
他们可以共享结构, reduces memory usage . 例如,考虑Haskell中的列表[1,2,3,4]和Java等命令式语言 . 要在Haskell中生成新列表,您只需创建新的
cons
(值对和引用到下一个元素)并将其连接到上一个列表 . 在Java中,您必须创建全新的列表,以免损坏前一个列表 .您可以创建持久数据结构 lazy .
,如果你使用功能样式,你可以 avoid thinking of time and sequence of operations ,等等,让你的程序更多declarative .
事实上,数据结构是不可变的,允许你做出一些更多的假设,所以 expand capabilities of language . 例如,Clojure使用不变性的事实来正确地为每个对象提供hashCode()方法的实现,因此任何对象都可以用作映射中的键 .
具有不可变数据和功能样式,您也可以自由使用 memoization .
一般来说,它有更多的优点,它是对现实世界进行建模的另一种方式 . This和SICP的其他章节将为您提供更准确的不可变结构编程视图,优缺点 .
除了共享内存安全性之外,大多数纯函数数据结构还可以为您提供持久性,实际上是免费的 . 例如,假设我在OCaml中有一个
set
,我想为它添加一些新值我可以这样做:a
在添加新字符后保留 unmodified (它只包含a-d),而b
包含a-h,它们共享一些相同的内存(set
它是一个AVL树,树的形状发生变化) . 我可以继续这样做,跟踪我对树所做的所有更改,让我回到以前的状态 .这是Wikipedia article on Purely Functional的一个很棒的图表,它显示了将字符'e'插入二叉树
xs
的结果:Erlang程序几乎完全使用纯功能数据结构,并且通过几乎无缝扩展到多个内核,它们获得了巨大的好处 . 由于永远不会修改共享数据(主要是二进制文件和位字符串),因此无需锁定此类数据 .
拿这个F#的小片段:
您可以100%确定地说这是整数1到5的不可变列表 . 您可以传递对该列表的引用,并且永远不必担心列表可能已被修改 . 这足以让我使用它 .
纯功能数据结构具有以下优点:
持久性:旧版本可以安全地重复使用,因为它们无法更改 .
共享:只需要适度的内存要求,就可以同时保存多个版本的数据结构 .
线程安全:任何突变都隐藏在懒惰的thunk中(如果有的话),因此由语言实现处理 .
简单性:不必跟踪状态变化,使纯粹的功能数据结构更易于使用,特别是在并发环境中 .
增量:纯功能数据结构由许多微小部分组成,使其成为增量垃圾收集的理想选择,从而降低延迟 .
请注意,我没有将并行性列为纯功能数据结构的优势,因为我不相信这种情况 . 高效的多核并行性需要可预测的局部性,以便利用高速缓存并避免在对主存储器的共享访问上遇到瓶颈,而纯功能数据结构在这方面至多具有未知特性 . 因此,许多使用纯功能数据结构的程序在多核上并行化时不能很好地扩展,因为它们将所有时间都花在缓存未命中,争夺共享内存路径 .
这里有一些混乱 . 在纯函数数据结构的上下文中,持久性是一个术语,用于指代在知道它们仍然有效的情况下引用回安全的数据结构的先前版本的能力 . 这是纯功能的自然结果,因此,持久性是所有纯功能数据结构的固有特征 .