如何在Haskell中的数据结构中获得恒定时间访问(如在数组中)?

时间:2013-11-07 09:13:07

标签: haskell

我会直接了解它 - 有没有办法在Haskell中拥有动态大小的常量时间访问数据结构,就像任何其他命令式语言中的数组一样?

我确信在某个地方有一个模块可以为我们神奇地做到这一点,但我希望能够以一种功能性的方式对一个人如何做到这一点的一般性解释:)

据我所知,Map使用二叉树表示,因此它具有 O(log(n)) 访问时间,当然列表 {{1访问时间。

另外,如果我们这样做它是不可变的,它会是纯粹的,对吧?

我有什么想法可以解决这个问题(超出模板Haskell之类的O(n)之类的东西)?

4 个答案:

答案 0 :(得分:7)

如果您的密钥与Int同构,那么您可以使用IntMap,因为其大多数操作都是O(min(n,W)),其中n是元素的数量,{{1} {}}中的位数(通常为32或64),这意味着当集合变大时,每个单独操作的成本会收敛到常量。

答案 1 :(得分:5)

  

Haskell中动态大小的常量时间访问数据结构,

  • Data.Array
  • Data.Vector

等等。

对于关联结构,您可以选择:

  • Log-N树和特里结构
  • 哈希表
  • 混合哈希映射尝试

具有各种不同的对数复杂度和常数因子。

所有这些都是关于黑客的。

答案 2 :(得分:2)

除了其他好的答案之外,可能有用的说法是:

  

当限于代数数据类型和纯度时,全部动态   大小的数据结构必须至少具有对数最坏情况访问   时间。

就个人而言,我喜欢将此称为纯度价格

Haskell为您提供了三种主要方法:

  • 更改问题:使用哈希值或前缀树。
  • 对于固定时间读取,请使用纯Arrays或更新的Vectors;它们不是ADT,需要内部的编译器支持/隐藏IO。由于纯度禁止修改原始数据结构,因此无法进行恒定时间写入。
  • 对于固定时间写入,请使用IOST monad,在可以避免外部可见副作用时更喜欢ST。这些monad在编译器中实现。

答案 3 :(得分:1)

确实,没有编译器/运行时魔术,就无法在Haskell中拥有恒定时间的访问数组。

但是,这不是(仅仅是)因为Haskell具有功能。 Java和C#中的数组也需要运行时魔术。在Rust中,您也许可以用不安全的代码来实现它们,但不能在安全的Rust中实现。

事实是,任何一种不允许您分配动态大小的内存,或者不允许您使用指针的语言都将需要运行时魔术来实现数组。

这不包括任何安全语言,无论是面向对象的还是功能的。

Haskell和eg之间的唯一区别。就数组而言,Java在Haskell中远没有Java有用,但是在Java数组中,数组是我们所做的一切的核心,因此我们甚至都没有注意到它们是魔术。

虽然Haskell对数组比例如eg需要更多的魔术,但是有一种方法。 Java。

使用Java,您可以初始化一个空数组(需要魔术),然后用值填充(不需要)。

使用Haskell,这显然会违反不变性。因此,任何数组都必须使用其值进行初始化。因此,编译器的魔力不仅仅在于为您提供空的内存块供您索引。它还要求您提供一种使用值初始化数组的方法。因此,数组的创建和初始化必须是一个步骤,完全由编译器处理。