在具有 O(1) 元素访问的 Haskell 中实现高效的拉链式数据结构

Posted

技术标签:

【中文标题】在具有 O(1) 元素访问的 Haskell 中实现高效的拉链式数据结构【英文标题】:Implementing efficient zipper like data structure in Haskell with O(1) elements access 【发布时间】:2013-11-25 03:40:09 【问题描述】:

问题

我想创建一个数据类型,它允许快速访问和修改其元素。是否可以在 Haskell 中创建一个结构和函数,其执行速度与简单的 C++ 实现一样快?

问题详情

我正在用 Haskell 编写一个编译器。我有 AST 由一个数据类型表示,让我们考虑以下一个:

import Prelude hiding (id)

-- this is a sample data type, the real one has got a lot of constructors
data AST = A  id :: Int, x :: AST, y :: AST, z :: AST 
         | B  id :: Int 
         | C  id :: Int, x :: AST, y :: AST 
         | D  id :: Int, u :: AST, v :: AST, w :: AST

每个 AST 节点都有一个唯一标识符。我很想在 Haskell 中实现以下功能:

一个函数getById,它将以O(1)的时间复杂度返回一个选定ID的AST节点。 能够在结构上创建“焦点”并相互独立地修改焦点元素。所以我希望能够记住一些子树的焦点,并能够以O(1) 时间复杂度修改每个这样的焦点。

我在考虑Zippers,但是他们有3个问题:

    (据我所知)它们与简单的数据类型一起使用,例如二叉树,我们可以说,我们选择“左”或“右”分支。是否有任何简单的方法可以在上述复杂数据类型上使用它们? 我认为他们不会允许我以 O(1)时间复杂度实现函数 getById,对吗? 我认为使用 Zippers 创建一些独立的焦点是不可能的。我所说的独立焦点是指焦点,这将允许我们修改数据类型的不同部分,而无需重新计算其他焦点(在 O(1) 中)。

C++ 的思维方式

在 C++ 中,我们将能够创建指向 AST 节点 nodePtrs 的指针数组。函数nodeById 将在O(1) 中执行,只需访问*(nodePtrs[id])。因为 C++ 结构是可变的,所以我们可以在 O(1) 中不受任何限制地修改它的元素。

【问题讨论】:

变异在被控制之前还不错。 Haskell 有许多提供可变数据结构的包,例如 MVector 您应该尝试进入 Haskell 思维模式。改变你的 AST 不是 Haskell 的方式。 @Ankur:我更愿意按照 augustss 的建议使用功能性方式。可变方式是唯一获得与 C++ 相同速度的方式吗? @augustss:我也有这种感觉,但是如何才能用不可变的数据获得同样的速度呢? @danilo2 你是用 Haskell 的方式做的,发现太慢了吗? 【参考方案1】:

我认为拉链实际上总是可以实现的,你听说过differentiation吗?

好吧,关于getById,我不确定这是个好主意。您可能想要一个像 getById :: Int -> IO AST 这样的 Haskell 函数,它在数组或其他东西中使用查找。但是由于您以后希望能够修改值(至少在概念上),您希望getById 返回新修改的 AST 值还是存储的第一个 AST 值?这一切都变得有问题。看看你是否可以删除你的 haskell 版本的 ID 可能是个好主意。

我认为你的关注点听起来可行。如果我们说ZAST 是 AST 的拉链数据类型。那么你也许可以有类似的东西。

makeFocus :: ZAST -> Focus ZAST

type Focus =
  (ZAST -> ZAST) -> -- The modifier of the "below part"
  ZAST ->           -- The new "above part", you have to provide it again as it might have changed
  ZAST              -- The Result

但是是的,它并不像 C++ 方式那样方便。


总之,我认为您应该退后一步,看看您实际尝试做的事情(AST 优化、发射程序集等)是否可以使用不可变数据结构有效地完成。而不是固执地尝试在 Haskell 中实现可变 C++ 数据结构的相同规范。

【讨论】:

以上是关于在具有 O(1) 元素访问的 Haskell 中实现高效的拉链式数据结构的主要内容,如果未能解决你的问题,请参考以下文章

文件处理-Haskell

如何在 Javascript 中实现 Haskell 的 FRP Behavior 类型?

如何在 Haskell 中实现 B+ 树?

haskell中的数组访问速度慢?

在 Haskell 中实现 Smullyan 的算术鸟

在 IRC 机器人 (Haskell) 中实现 CTCP 命令