Haskell 集合保证每个操作的最坏情况界限?

标签 haskell collections amortized-analysis

这种结构对于实时应用程序是必要的——例如用户界面。 (用户不关心单击按钮需要 0.1 秒还是 0.2 秒,但他们确实关心第 100 次单击是否会强制执行出色的惰性计算并需要 10 秒才能继续。)

我在看冈崎的论文Purely functional data structures他描述了一种有趣的通用方法,用于将具有摊销边界的惰性数据结构转换为具有相同 的结构。每个操作的最坏情况界限 .这个想法是分配计算,以便在每次更新时强制执行一些未评估的 thunk。

我想知道,Haskell 中是否有任何标准集合( MapSet 等)的实现?

containers包说

The declared cost of each operation is either worst-case or amortized, but remains valid even if structures are shared.



因此不能保证单个操作的最坏情况界限。有严格的变体,如 Data.Map.Strict ,但它们的键和值很严格:

Key and value arguments are evaluated to WHNF; Keys and values are evaluated to WHNF before they are stored in the map.



它的结构没有(可能的)严格性。

最佳答案

there is nothing about (possible) strictness of its structure.



去寻找来源,例如对于 Data.Map.Map
-- See Note: Order of constructors
data Map k a  = Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a)
              | Tip

您会看到 Map 完全是脊椎严格的(并且在键中是严格的,即使使用 Data.Map.Lazy 也是如此),如果您将其评估为 WHNF,则强制使用完整的脊椎。这同样适用于 IntMap s、Set s 和 IntSet s。

因此,您可以通过在每次操作之前将容器强制为 WHNF 来轻松地防止构建大型 thunk(映射到/包含的值除外)。对于 Data.XYZ.Strict 变体,防止包含值的大 thunk [时间(和空间)泄漏的常见原因] 是自动的(警告:这些值仅评估为 WHNF,如果您需要更多,您必须自己做例如 deepseq 在操作后立即更改任何值),您需要使用 Data.XYZ.Lazy 变体自行处理。

因此

Users don't care if clicking a button takes 0.1s or 0.2s, but they do care if the 100th click forces an outstanding lazy computation and takes 10s to proceed.



是这些容器很容易避免的问题。

但是,第 100 次点击的处理时间仍然可能比平均时间长得多,这不是由于出色的惰性计算,而是由于算法(考虑具有两个列表的经典队列实现,前面,您将元素从 dequeue (Q (x:xs) ys) = (x, Q xs ys) 出列在 O(1) 中,在 O(1) 中 enqueue y (Q xs ys) = Q xs (y:ys) 的后面,好吧,除了当前面列表为空并且后面需要先反转时,出队需要 O(size),但它是 O(1) 摊销的仍然)不改变摊余成本。

我不知道 containers 中使用的算法是否有这种情况,但这是需要注意的。

关于Haskell 集合保证每个操作的最坏情况界限?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12393104/

相关文章:

java - StringBuffer 和摊销

sorting - 堆排序时间复杂度深入理解

haskell - 这种自由(更自由?)单子(monad)的构造有效吗?

haskell - 非纯函数破坏可组合性是什么意思?

collections - C# 中的 IComparer<> 和类继承

javascript - 使用此 Node.js 字典从值中获取键

parsing - 如何在 Haskell 中组合解析器最多 n 次?

mongodb - 如何从处理程序访问全局 MongoDB 连接?

c# - 无需显式重新创建的 WPF 刷新 CollectionView(Refresh() 方法调用)

algorithm - 计算散列函数的摊销复杂度