在Haskell中使用O(1)元素访问实现高效的拉链,如数据结构

时间:2013-11-25 03:40:10

标签: c++ performance algorithm haskell zipper

问题

我想创建一个数据类型,以便快速访问和修改其元素。是否有可能在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个问题:

  1. 它们(据我所知)使用简单的数据类型,如二叉树,我们可以说,我们选择“左”或“右”分支。有没有简单的方法在复杂的数据类型上使用它们,如上面的那个?
  2. 我认为他们不会允许我以getById时间复杂度实现O(1)函数,我是对的吗?
  3. 我认为使用Zippers创建一些独立的焦点是不可能的。通过独立焦点,我的意思是焦点,这将允许我们修改数据类型的不同部分,而无需重新计算其他焦点(在O(1)中)。
  4. C ++的思维方式

    在C ++中,我们可以创建指向AST节点nodePtrs的指针数组。只需访问nodeById,函数O(1)即可在*(nodePtrs[id])中执行。由于C ++结构是可变的,我们可以在O(1)中无任何限制地修改其元素。

1 个答案:

答案 0 :(得分: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 ++数据结构的相同规范。