Repository navigation
Expand file tree
/
Copy pathQueue.hs
More file actions
97 lines (81 loc) · 2.42 KB
/
Copy pathQueue.hs
File metadata and controls
97 lines (81 loc) · 2.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
module Part03.Queue (module Part03.Queue) where
import Control.Monad
import Data.IORef
import qualified Data.Vector.Mutable as Vec
------------------------------------------------------------------------
-- In order to make the queue more performant and more tricky for ourselves to
-- implement, we'll make it array-backed where elements are not removed from the
-- array when they are dequeued, but rather overwritten by a later enqueue
-- operation.
-- In order to pull this off we'll keep track of the total capacity of the
-- queue, the current size and what the "rear" of the queue is, i.e. the index
-- which we should dequeue next.
data Queue a = Queue
{ qCapacity :: !Int
, qSize :: !(IORef Int)
, qRear :: !(IORef Int)
, qQueue :: !(Vec.IOVector a)
}
newQueue :: Int -> IO (Queue a)
newQueue cap = Queue <$> pure cap <*> newIORef 0 <*> newIORef 0 <*> Vec.new cap
capacity :: Queue a -> Int
capacity = qCapacity
size :: Queue a -> IO Int
size = readIORef . qSize
-- XXX: Don't export.
rear :: Queue a -> IO Int
rear = readIORef . qRear
clear :: Queue a -> IO ()
clear q = do
writeIORef (qSize q) 0
writeIORef (qRear q) 0
Vec.clear (qQueue q)
isEmpty :: Queue a -> IO Bool
isEmpty q = do
sz <- size q
return (sz == 0)
enqueue :: Queue a -> a -> IO Bool
enqueue q x = do
n <- size q
if n >= capacity q
then return False
else do
j <- rear q
Vec.unsafeWrite (qQueue q) ((j + n) `mod` capacity q) x
modifyIORef' (qSize q) succ
return True
dequeue :: Queue a -> IO (Maybe a)
dequeue q = do
empty <- isEmpty q
if empty
then return Nothing
else do
j <- rear q
x <- Vec.unsafeRead (qQueue q) j
modifyIORef' (qSize q) (\sz -> sz - 1)
modifyIORef' (qRear q) (\j' -> (j' + 1) `mod` capacity q)
return (Just x)
-- We add a display function for debugging.
display :: Show a => Queue a -> IO ()
display q = do
putStrLn "Queue"
putStr " { capacity = "
putStrLn (show (capacity q))
putStr " , size = "
sz <- size q
putStrLn (show sz)
putStr " , rear = "
r <- rear q
putStrLn (show r)
putStr " , queue = "
putStr "["
r' <- rear q
sz' <- size q
flip mapM_ [r'..sz' - 1] $ \ix -> do
x <- Vec.unsafeRead (qQueue q) ix
putStr (show x)
unless (ix == sz' - 1) $ do
putStr ", "
putStrLn "]"
-- If you read this far, hopefully you will appreciate that getting all this
-- right without an off-by-one error somewhere can be a bit tricky...