此类结构对于实时应用程序是必需的 - 例如用户界面。 (如果点击按钮需要0.1秒或0.2秒,用户不在乎,但是如果第100次点击强制执行一个非常懒的计算并且需要10秒才能继续,他们会关心。)
我正在阅读Okasaki的论文Purely functional data structures,他描述了一种有趣的通用方法,用于将具有摊销边界的惰性数据结构转换为具有每个操作的最坏情况边界的结构即可。这个想法是分配计算,以便在每次更新时强制部分未评估的thunk。
我想知道,Haskell中是否有任何此类标准集合(Map
,Set
等)的实现?
每项操作的申报成本是最坏情况或摊销,但即使共享结构仍然有效。
因此无法保证单个操作的最坏情况限制。有像Data.Map.Strict
这样的严格变体,但它们的键和值都很严格:
键和值参数被评估为WHNF;键和值在存储到地图之前将被评估为WHNF。
没有关于(可能)严格的结构。
答案 0 :(得分:11)
没有关于(可能)严格的结构。
去寻找来源,例如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
,Set
和IntSet
s。
因此,您可以通过在每次操作之前将容器强制转换为WHNF来轻松地防止构造大型thunk(除了映射到/包含的值)。对于Data.XYZ.Strict
变体,可以自动防止所包含的值[时间(和空间)泄漏的常见原因)的大量thunk(警告:值仅评估为WHNF,如果需要更多,则必须通过例如{{1}运行后immediatley的任何更改值来自己完成),你需要使用deepseq
变体处理自己。
因此
用户不关心点击按钮需要0.1秒或0.2秒,但如果第100次点击强制执行一项非常懒的计算并需要10秒才能继续,则用户不在乎。
是这些容器容易避免的问题。
然而,它仍然可能是第100次点击需要多处理比平均值更长的时间,不是由于优秀的延迟计算,而是由于算法(考虑具有两个列表的经典队列实现) ,前面,你在O(1)中通过Data.XYZ.Lazy
将元素出列,以及在O(1)中你dequeue (Q (x:xs) ys) = (x, Q xs ys)
的后面,好吧,除了在前面列表中出列需要O(大小)是空的,后面需要先逆转,但仍然是O(1)摊销而不改变摊余成本。
我不知道containers中使用的算法是否有任何此类情况,但需要注意的事项。