这是我在FIFO队列中的尝试:
type Queue a = [a] -> [a]
empty :: Queue a
empty = id
remove :: Int -> Queue a -> ([a], Queue a)
remove n queue = (take n (queue []), (\x -> drop n (queue x)));
add :: [a] -> Queue a -> Queue a
add elems queue = (\x -> queue (elems ++ x))
empty
创建一个空队列,remove
获取队列的第一个n
元素,并返回队列的其余部分作为元组的第二个元素,{{1}将列表add
添加到队列中。
这会在elems
时间内添加/删除1个元素,在O(1)
时间内添加/删除n个元素吗?
答案 0 :(得分:6)
您实施的内容相当于difference lists。 (见:dlist。)
差异列表允许便宜的追加,但不幸的是,您的删除将需要线性时间。如果我们稍微重写您的代码就会变得更加清晰:
type Queue a = [a] -> [a]
empty :: Queue a
empty = id
toList :: Queue a -> [a]
toList q = q []
fromList :: [a] -> Queue a
fromList = (++)
remove :: Int -> Queue a -> ([a], Queue a)
remove n q = (xs, fromList ys)
where
(xs, ys) = splitAt n (toList q)
add :: [a] -> Queue a -> Queue a
add xs q = (++ xs) . q
请注意,我在列表中的转换比在代码中更明确。您清楚地看到删除代码的核心在toList
和fromList
之间被置于括号内。
答案 1 :(得分:4)
好吧,稍微回避你的问题,FIFO队列的经典纯功能实现是一对列表,一个用于“前”,一个用于“后”。您通过将元素添加为后面列表的头部来排队元素,并通过前面列表的头部出列;如果前面列表为空,则通过反转后退列表并使用空前面列表交换来“旋转”队列。在代码中:
import Control.Monad
import Data.List
import Data.Maybe
data FIFO a = FIFO [a] [a]
deriving Show
empty :: FIFO a
empty = FIFO [] []
isEmpty :: FIFO a -> Bool
isEmpty (FIFO [] []) = True
isEmpty _ = False
enqueue :: a -> FIFO a -> FIFO a
enqueue x (FIFO front back) = FIFO front (x:back)
-- | Remove the head off the queue. My type's different from yours
-- because I use Maybe to handle the case where somebody tries to
-- dequeue off an empty FIFO.
dequeue :: FIFO a -> Maybe (a, FIFO a)
dequeue queue = case queue of
FIFO [] [] -> Nothing
FIFO (x:f) b -> Just (x, FIFO f b)
otherwise -> dequeue (rotate queue)
where rotate (FIFO [] back) = FIFO (reverse back) []
-- | Elements exit the queue in the order they appear in the list.
fromList :: [a] -> FIFO a
fromList xs = FIFO xs []
-- | Elements appear in the result list in the order they exit the queue.
toList :: FIFO a -> [a]
toList = unfoldr dequeue
这是经典的实现。现在您的操作可以写成:
-- | Enqueue multiple elements. Elements exit the queue in the order
-- they appear in xs.
add :: [a] -> FIFO a -> FIFO a
add xs q = foldl' (flip enqueue) q xs
要根据remove
来编写dequeue
,您需要处理来自(a, FIFO a)
dequeue
结果的所有中间FIFO。一种方法是使用State
monad:
import Control.Monad.State
-- | Remove n elements from the queue. My result type is different
-- from yours, again, because I handle the empty FIFO case. If you
-- try to remove too many elements, you get a bunch of Nothings at
-- the end of your list.
remove :: Int -> FIFO a -> ([Maybe a], FIFO a)
remove n q = runState (removeM n) q
-- | State monad action to dequeue n elements from the state queue.
removeM :: Int -> State (FIFO a) [Maybe a]
removeM n = replicateM n dequeueM
-- | State monad action to dequeue an element from the state queue.
dequeueM :: State (FIFO a) (Maybe a)
dequeueM = do q <- get
case dequeue q of
Just (x, q') -> put q' >> return (Just x)
Nothing -> return Nothing