2012-07-12 88 views

回答

6

实际上,在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)。无论对insget的调用是如何交错的 - 在两个调用中,每个元素都被调用一次,移动一次并且被拒绝一次,相同的论证仍然有效。

+0

谢谢,我喜欢你的帖子。为了避免出现错误,您必须在每次获取元素前检查列表是否为空,是不是?这是否比改变得到的类型更好:: q a - >(也许a,q a)? – efie 2012-07-12 12:23:39

+3

'安全'的方式是将类型改为'get :: qa - > Maybe(a,qa)'(用'Maybe'包装元组,因为它没有意义返回剩余的队列如果我们没有检索到第一个元素)。为了保持实现的简单性,我忽略了这一点,因此在使用代码之前,您必须先检查列表是否为空,然后才能安全使用它。请注意,这不是更多的工作,因为使用'Maybe'包装器,我们必须在调用之后检查'Nothing' *。但是,使用包装我们被类型系统强制检查 - 没有它,你可以编写不安全的代码。 – 2012-07-12 12:27:22

+0

我想详细说明获取元素的平均情况如何仅为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

2

取决于你想要的操作。 queuelikedequeue包具有队列的类型类。