是否有人编写了描述FIFO队列的Haskell类型类(或者是否有类型类的组合)。队列的Haskell类型
Data.Collection.Sequence似乎太强大,但另一方面Data.Collection.Unfoldable似乎太弱(顺序未定义)。
我只是想不重做别人的工作。
是否有人编写了描述FIFO队列的Haskell类型类(或者是否有类型类的组合)。队列的Haskell类型
Data.Collection.Sequence似乎太强大,但另一方面Data.Collection.Unfoldable似乎太弱(顺序未定义)。
我只是想不重做别人的工作。
实际上,在Haskell中推出自己的FIFO队列并不难(也是一个有趣的练习)。我可以欣赏你希望为此使用标准类型类,这几乎可以肯定你应该做的。但是我刚刚在上周了解到了这一点,我很兴奋不写这些。
下面是一个简单的队列类,它允许您检查队列是否为空,从队列的头部获取第一个元素(并返回队列的其余部分)并将新元素插入到队列中。
class Queue q where
empty :: q a -> Bool
get :: q a -> (a, q a)
ins :: a -> q a -> q a
做出FIFO队列的最简单方法是使用一个列表:
instance Queue [] where
empty = null
get [] = error "Can't retrieve elements from an empty list"
get (x:xs) = (x, xs)
ins x xs = xs ++ [x]
然而,这是可怕的效率低下。如果队列当前有n元素,则插入一个新元素需要O(n)时间。如果要将m元素插入空队列,则需要花费O(m )时间。我们可以创建一个队列来插入和检索O(1)时间内的元素(或者至少,O(1)摊销时间)?
诀窍是存储的前部和在单独的列表的队列的后面,与队列的后面被存储在反向:
data Fifo a = F [a] [a]
instance Queue Fifo where
队列是空的,如果正面和背面是空:
empty (F xs ys) = null xs && null ys
要插入一个新的元素到列表中,我们只是把它放在后面的队列中,这需要O(1)次。
ins y (F xs ys) = F xs (y:ys)
获取元素从队列的前部很容易当有等待在那里元件(以及我们抛出一个错误,如果队列为空)
get (F [] []) = error "Can't retrieve elements from an empty queue"
get (F (x:xs) ys) = (x, F xs ys)
最后,如果没有元件在队列的前面等待,然后我们将队列的后面颠倒放在前面。虽然这需要O(ñ)的时候,我们只需要为每个元素做一次,所以我们get操作平均到O(1)时间:
get (F [] ys) = get (F (reverse ys) [])
你有它 - 摊销O( 1)以功能语言的FIFO队列。
编辑: EFIE问及摊销Ø在评论(1)性能。分摊恒定时间的论点非常简单。
考虑的Ñ插入到一个空队列的顺序,接着Ñ检索。插入花费时间n。在第一次检索时,队列的前面是空的,所以我们必须倒转队列的后面,这也需要时间n,再加上1来检索元素。最后,下Ñ - 1检索需要时间1中的每个,所以总时间是
Ñ + Ñ + 1 + Ñ - 1 = 3 Ñ
我们做了2 n电话总数,所以摊销时间是3 n/2 n = 3/2,这是O(1)。无论对ins
和get
的调用是如何交错的 - 在两个调用中,每个元素都被调用一次,移动一次并且被拒绝一次,相同的论证仍然有效。
谢谢,我喜欢你的帖子。为了避免出现错误,您必须在每次获取元素前检查列表是否为空,是不是?这是否比改变得到的类型更好:: q a - >(也许a,q a)? – efie 2012-07-12 12:23:39
'安全'的方式是将类型改为'get :: qa - > Maybe(a,qa)'(用'Maybe'包装元组,因为它没有意义返回剩余的队列如果我们没有检索到第一个元素)。为了保持实现的简单性,我忽略了这一点,因此在使用代码之前,您必须先检查列表是否为空,然后才能安全使用它。请注意,这不是更多的工作,因为使用'Maybe'包装器,我们必须在调用之后检查'Nothing' *。但是,使用包装我们被类型系统强制检查 - 没有它,你可以编写不安全的代码。 – 2012-07-12 12:27:22
我想详细说明获取元素的平均情况如何仅为O(1)。如果你的队列中有n个元素,则有(n + 1)!将它们分配到两个列表的方法:n! (n + 1)个方法来将每个排列分成两个列表。有n!如果xs为空,则在ys中排列元素的方法。现在操作的数量组成如下:prob(“xsIsEmpty”)* costs(“xsIsEmpty”)+ prob(“xsNotEmpty”)* costs(“xsNotEmpty)= n!/(n + 1)!* n + ... * 0 = n/n + 1 = O(1)。 – efie 2012-07-12 12:39:56