haskell中的O(1)循环缓冲区?

时间:2010-02-08 16:12:26

标签: haskell functional-programming circular-buffer

我正在研究Haskell中的一个小概念项目,它需要一个循环缓冲区。我已经设法使用具有O(1)旋转的数组创建缓冲区,但当然需要O(N)来插入/删除。我发现使用列表的实现似乎需要O(1)进行插入和删除,但由于它保持左右列表,因此在旋转时越过某个边界将花费O(N)时间。在命令式语言中,我可以实现具有O(1)插入,删除和旋转的双向链接循环缓冲区。我认为这在像Haskell这样的纯函数式语言中是不可能的,但是我想知道我是不是错了。

3 个答案:

答案 0 :(得分:11)

如果您可以处理分摊的 O(1)操作,您可以使用容器包中的Data.Sequence或来自dequeue包的Data.Dequeue。前者使用finger trees,而后者使用Okasaki的Purely Functional Data Structures(在线版本here)中的“Banker's Dequeue”。

答案 1 :(得分:4)

ST monad允许在Haskell中描述和执行命令式算法。您可以使用STRefs作为双向链表的可变指针。

使用ST执行使用runST描述的自包含算法。不同的runST个执行可能不会共享ST个数据结构(STRefSTArray,...)。

如果算法不是“自包含”且需要在其使用之间执行IO操作来维护数据结构,则可以使用stToIOIO monad中访问它。

关于这是否纯粹功能 - 我猜它不是?

答案 2 :(得分:2)

听起来你可能需要比这更复杂的东西(因为你提到了双链表),但也许这会有所帮助。此函数与可变循环列表的map类似:

mapOnCycling f = concat . tail . iterate (map f)

使用类似:

*Main> (+1) `mapOnCycling` [3,2,1]

[4,3,2,5,4,3,6,5,4,7,6,5,8,7,6,9,8,7,10,9...]

这是一个像mapAccumL:

的行为
mapAccumLOnCycling f acc xs = 
    let (acc', xs') =  mapAccumL f acc xs
     in xs' ++ mapAccumLOnCycling f acc' xs'

无论如何,如果你想详细说明你的数据结构究竟需要“做什么”,我真的很想听听它。

编辑:正如camccann所提到的那样,你可以使用Data.Sequence,根据文档应该给你O1的时间复杂度(是否存在O1摊销时间?)用于查看或添加序列左侧和右侧的元素,以及沿途修改末端。这是否具备您所需的性能,我不确定。

您可以将“当前位置”视为序列的左端。在这里,我们沿着一个序列来回穿梭,产生一个无限的价值列表。对不起,如果它没有编译,我目前没有GHC:

shuttle (viewl-> a <: as) = a : shuttle $ rotate (a+1 <| as)
    where rotate | even a    = rotateForward
                 | otherwise = rotateBack
          rotateBack (viewr-> as' :> a')    = a' <| as'
          rotateForward (viewl-> a' <: as') = as' |> a'