我正在尝试在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也不起作用。
答案 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
扩展名(MultiParamTypeClasses
和FunctionalDependencies
)来启用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将告诉您启用哪些扩展名),但我发现在使用通用容器时,这种方法通常不太灵活。