【问题标题】:What is the most efficient implementation of arrays with functional updates?具有功能更新的数组的最有效实现是什么?
【发布时间】:2016-12-30 21:34:28
【问题描述】:

我需要一个具有最快功能更新的类数组数据结构。我已经看到了一些灵​​活数组的不同实现,它们为我提供了这个属性(Braun,随机访问列表),但我想知道是否有专门针对我们对追加或前置不感兴趣的情况进行优化的实现- 只是更新。

【问题讨论】:

  • 肯定是某种地图?
  • @DominicBou-Samra 一张不可变的地图?不会比数组还贵吗?
  • Dominic、地图、Braun 和 RAL 都是基于树的。我正在寻找是否可以与命令式数组(未变异)进行一些巧妙的组合,可以击败基于纯树的数据结构。
  • 我想 HAMT (en.wikipedia.org/wiki/Hash_array_mapped_trie) 对你来说可能是个不错的选择。它基本上类似于 Haskell 的 IntMap,但通常呈现出更像数组的接口(参见例如 Scala 的 Vector 类或其在 Clojure 中的对应类)
  • copumpkin:作为 OCaml 的 HAMT 实现的主题评论已发布 just a few days ago。请参阅我的答案以获得更简单的持久数组实现,接近 Michael Day 提到的“版本数组”。

标签: haskell data-structures functional-programming ocaml immutability


【解决方案1】:

Jean-Cristophe Filliâtre 有a very nice implementation 的持久数组,这在同一页面链接的the paper 中进行了描述(这是关于持久联合查找,其中持久数组是核心组件)。代码可直接获取there。

这个想法是将数组的“最后一个版本”表示为一个通常的数组,O(1) 访问和更新操作,之前的版本表示为这个最后一个版本,加上一个差异列表。如果您尝试访问该结构的先前版本,则该数组将“重根化”以应用差异列表并再次为您提供有效的表示。

这当然不是所有工作流程下的 O(1)(如果您不断访问和修改结构的不相关版本,您将经常支付重新root 成本),但对于主要使用一个版本的常见工作流程,并且偶尔回溯到旧版本再次成为“最后一个版本”并获取更新,这是非常有效的。一个非常好的使用隐藏在观察纯界面下的可变性。

【讨论】:

    【解决方案2】:

    我对@9​​87654321@ (nice demo) 有很好的体验。非常好的性能,自动并行,多维,多态。推荐尝试。

    【讨论】:

      【解决方案3】:

      您使用哪种语言?在 Haskell 中,您可以将 mutable arrays 与状态 monad 一起使用,在 Mercury 中,您可以通过线程化 IO 状态来使用可变数组。 Ocaml 也有一个数组模块,不幸的是它不保持引用透明性,如果这是你所追求的。

      【讨论】:

      • 我正在使用 OCaml。我将问题标记为 Haskell,以利用社区对这些事物的了解。顺便说一句,我不确定 STArray 将如何解决我使用更新数组同时保持旧副本有效可用的问题。
      • OCaml 数组是可变的,它们没有持久性。如果不是不可能的话,具有更新和持久性的恒定时间访问似乎相当困难。所以,地图可能就是你想要的(如上面所建议的)。
      • 有许多不同的基于地图的实现,我正在寻找最适合我的用例的实现。例如,平衡的 BST 对这个应用程序来说是很糟糕的。虽然渐近性能是一样的。
      • Mercury 有版本数组,它提供 O(1) 访问数组的最新版本和 O(n) 访问旧版本,其中 n 是自旧版本。
      【解决方案4】:

      几天前我还需要函数数组,并在这个 SO 问题上有所感受。我对 Gasche 提出的解决方案不满意,因为创建新阵列是一项昂贵的操作,而且我需要经常访问旧版本的阵列(我计划将其用于在阵列上播放的 AI alpha/beta 实现)。

      (当我说代价高昂时,我猜它是 O(n*h),其中 h 是历史大小,因为在最坏的情况下,只有一个单元格被重复更新,并且需要遍历每个单元格的整个更新列表。我还希望当我需要重新路由阵列时,大多数单元格不会被更新)。

      这就是我提出另一种方法的原因,也许我可以在这里得到一些反馈。我的想法是将数组存储在 B-Tree 中,除了它不可变,我可以很容易地通过索引访问和更新任何值。

      我在项目的存储库上写了一个小介绍:https://github.com/shepard8/ocaml-ptarray。选择顺序是为了使树的深度和顺序均匀,因此我可以获得很好的复杂性,仅取决于获取/设置操作的顺序,即 O(k^2)。

      如果 k = 10,我最多可以存储 10^10 个值。实际上,我的数组不应包含超过 200 个值,但这是为了显示我的解决方案的稳健性。

      欢迎任何建议!

      【讨论】:

        猜你喜欢
        • 2018-03-20
        • 2021-08-02
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        • 1970-01-01
        • 1970-01-01
        • 2020-01-25
        • 1970-01-01
        相关资源
        最近更新 更多