在haskell中使用平衡列表进行排队

时间:2017-01-09 16:32:59

标签: list haskell queue instance

我正在尝试在haskell中编写平衡列表的定义。 QueueA应该是具有平衡列表的队列:

module QueueA where 

import Queue

data QueueA a = QA [a] [a]

instance Queue (Queue a) where
    empty = QA [] []

    (|>) (QA a b) el = norm $ QA a (el : b)

    head (QA a _) = Prelude.head a

    tail (QA a b) = norm $ QA (Prelude.tail a) b

    toList queue = Queue.head queue : Queue.tail queue


norm :: [a] -> [a]
norm queue@(QA a b) = if (length a) >= (length b) then queue else QA (a ++ reverse b) []

基于接口队列

module Queue where

class Queue a where
     empty :: a
     (|>) :: a -> a -> a
     null :: a -> Bool
     head :: a -> a
     tail :: a -> a
     toList :: a -> [a]

我不知道如何正确定义界面,我基于我发现的一个例子。大多数错误看起来像这样:

 Couldn't match expected type `a' with actual type `QueueA a1'
  `a' is a rigid type variable bound by
      the instance declaration
      at C:\Users\kroll\Dropbox\WS 16.17\Moderne Funktionale Programmierung\Übung\Blatt 8\Code\Queue

我知道定义empty :: a可能是错误的,但是empty :: Queue a也不起作用。

2 个答案:

答案 0 :(得分:3)

我认为@ bheklilr的回答比必要的复杂得多。我认为在纯Haskell 98中可以给出一个足够好的队列类定义:

class Queue q where
    empty :: q a
    push :: a -> q a -> q a
    pop :: q a -> Maybe (a, q a)

不需要类型系列或函数依赖项,除非您需要执行高级mono-traversable - 类似技巧。

你班上的其他功能可以通过这个牙膏管挤压:

-- it may make sense to make this into a class method with a default implementation,
-- so that instances can give an optimised implementation where possible
null :: (Eq (q a), Queue q) => q a -> Bool
null q = q == empty

-- head and tail are unsafe! I don't recommend writing an interface like this
head :: Queue q => q a -> a
head = fst . fromJust . pop

tail :: Queue q => q a -> q a
tail = snd . fromJust . pop

(|>) :: Queue q => q a -> a -> q a
(|>) = flip push

toList :: Queue q => q a -> [a]
toList = unfoldr pop

您的先进先出队列可以直接成为Queue的实例:

instance Queue QueueA where
    empty = QA [] []

    push x (QA front back) = QA front (x:back)

    pop (QA [] []) = Nothing
    pop (QA [] back) =
        let (x:xs) = reverse back
        in Just (x, QA xs [])
    pop (QA (x:front) back) = Just (x, QA front back)

......和先进先出队列一样:

newtype LIFO a = LIFO [a]

instance Queue LIFO where
    empty = LIFO []
    push x (LIFO xs) = LIFO (x:xs)
    pop (LIFO []) = Nothing
    pop (LIFO (x:xs)) = Just (x, LIFO xs)

如果要在抽象中包含优先级队列,它会变得更复杂,因为优先级队列需要知道如何选择优先级最高的元素,并且给定的接口无法比较元素。修补Queue的一种方法是使用ConstraintKinds扩展名(MultiParamTypeClassesFunctionalDependencies)来启用q来指定它对其设置的约束元素:

class Queue c q | q -> c where
    empty :: c a => q a
    push :: c a => a -> q a -> q a
    pop :: c a => q a -> Maybe (a, q a)

然后我们可以使用例如来自Playing With Priority Queues倾斜堆来实现优先级队列...

instance Queue Ord SkewHeap where  -- SkewHeap requires that its elements be an instance of Ord
    empty = Empty
    push x q = q `merge` SkewNode x Empty Empty
    pop Empty = Nothing
    pop (SkewNode x l r) = Just (x, l `merge` r)

...并且可以调整前两个队列以使用新的Queue ...

class Trivial a
instance Trivial a

instance Queue Trivial QueueA where
    {- as before -}
instance Queue Trivial LIFO where
    {- as before –}

答案 1 :(得分:2)

我想你想要像

这样的东西
class Queue q where
    type Item q
    empty :: q
    (|>) :: q -> Item q -> q
    null :: q -> Bool
    head :: q -> Item q
    tail :: q -> q
    toList :: q -> [Item q]

使用实例

instance Queue (QueueA a) where
    type Item (QueueA a) = a

    empty = QA [] []

    (QA a b) |> e = norm $ QA a (e : b)

    ...

请注意,这需要TypeFamilies扩展名。

错误的原因是因为您将队列本身与队列中的值混为一谈。例如,你有

instance Queue (QueueA b) where  -- changed (QueueA a) to (QueueA b) for clarity
    head :: QueueA b -> QueueA b
    head ... = ...

自您的实例中a ~ QueueA b起。但是,您正在使用b返回Prelude.head,因此会出错。你也可以选择

class Queue q item where
    empty :: q item
    (|>) :: q item -> item -> q item
    null :: q item -> Bool
    head :: q item -> item
    tail :: q item -> q item
    toList :: q item -> [item]

使用MultiParamTypeClasses和co(GHC将告诉您启用哪些扩展名),但我发现在使用通用容器时,这种方法通常不太灵活。