131018.2 · 6a302f15b2ac2fa54f1db6e9788eb1e91a4e7501
update
reading declarations
@@ -16,7 +16,7 @@ build-type: Simple cabal-version: >=1.8 library- exposed-modules: Monads.MRand, Monads.Dot, QC.SkewHeap, QC.Q, QC.PQueue, QC.QueueProps, QC.Heap+ exposed-modules: Monads.MRand, Monads.Dot, QC.SkewHeap, QC.Q, QC.PQueue, QC.Heap, QC.HeapProps -- other-modules: build-depends: base ==4.6.*, random ==1.0.*, QuickCheck ==2.6.*
@@ -2,11 +2,20 @@ build: cabal build cp ./dist/build/heapvis/heapvis ./bin/ +clean:+ rm -rf dist+ cabal configure+ docs: cabal haddock --hyperlink-source --executables+ rscp ./dist/doc/html/150fp-practice Geils:~/www/burntsushi.net/public_html/docs/haddock dot-heap:- bash -c 'imgv <(./bin/heapvis heap | dot -Tpng)'+ bash -c 'imgv -increment 100 <(./bin/heapvis heap | dot -Tpng)' dot-skew:- bash -c 'imgv <(./bin/heapvis skew | dot -Tpng)'+ bash -c 'imgv -increment 100 <(./bin/heapvis skew | dot -Tpng)'++push:+ git push origin master+ git push github master
@@ -13,8 +13,12 @@ import Text.Printf (printf) -- | @Node@ corresponds to any value representable as a Dot node. class Node a where+ -- | A uniquely identifying string for this node. name :: a -> String- label :: a -> Maybe String -- labels may not contain double quotes++ -- | Labels cannot contain double quotes. If no label is specified,+ -- then `name` will be used instead.+ label :: a -> Maybe String -- | Every edge is a directed edge from one node to another. data Edge a = Edge { from :: a, to :: a, elabel :: Maybe String }@@ -26,9 +30,21 @@ instance Node SimpleNode where name = sname label = slabel +-- | Create a new @SimpleNode@ with a name.+simpleNode :: String -- ^ The name of the node.+ -> SimpleNode+simpleNode name = SimpleNode name Nothing +-- | Same as `simpleNode`, but allows a label independent of the name.+simpleNodeLabel :: String -- ^ The name of the node.+ -> String -- ^ The label of the node.+ -> SimpleNode+simpleNodeLabel name label = SimpleNode name (Just label)++-- | @Graph@ corresponds to a graph expressible in the Dot language. data Graph a = Graph { nodes :: [a], edges :: [Edge a] } +-- | Converts a graph to its Dot representation. dot :: Node a => Graph a -> String dot (Graph ns es) = printf "digraph G {\n\t%s\n\t%s\n}" nodes edges where nodes = intercalate "\n\t" $ map dnode ns@@ -44,6 +60,7 @@ instance Monoid (Graph a) where mempty = Graph [] [] (Graph ns1 es1) `mappend` (Graph ns2 es2) = Graph (ns1 ++ ns2) (es1 ++ es2) +-- | A monad that constructs values representable in the Dot language. newtype Dotter n a = Dotter { runDotter :: (a, Graph n) } instance Monad (Dotter n) where@@ -51,30 +68,25 @@ instance Monad (Dotter n) where Dotter (a, g) >>= f = Dotter (a', g `mappend` g') where (a', g') = runDotter (f a) -simpleNode :: String -> SimpleNode-simpleNode name = SimpleNode name Nothing--simpleNodeLabel :: String -> String -> SimpleNode-simpleNodeLabel name label = SimpleNode name (Just label)-+-- | Adds a node to the current graph. addNode :: Node a => a -> Dotter a () addNode node = Dotter ((), Graph [node] []) -addEdge :: Node a => a -> a -> Dotter a ()+-- | Adds a directed edge to the current graph.+addEdge :: Node a+ => a -- ^ The "from" node.+ -> a -- ^ The "to" node.+ -> Dotter a () addEdge n1 n2 = Dotter ((), Graph [] [Edge n1 n2 Nothing]) -addEdgeLabel :: Node a => a -> a -> String -> Dotter a ()+-- | Same as `addEdge`, but with an edge label.+addEdgeLabel :: Node a+ => a -- ^ The "from" node.+ -> a -- ^ The "to" node.+ -> String -- ^ The edge label.+ -> Dotter a () addEdgeLabel n1 n2 label = Dotter ((), Graph [] [Edge n1 n2 (Just label)]) +-- | Evaluate an instance of the `Dotter` monad to its Dot representation. eval :: Node a => Dotter a () -> String eval = dot . snd . runDotter--testGraph :: Dotter SimpleNode ()-testGraph = do- let (n1, n2) = (simpleNode "A", simpleNode "B")- addNode n1- addNode n2- addEdge n1 n2--main :: IO ()-main = putStrLn $ eval testGraph
@@ -6,6 +6,7 @@ where import qualified System.Random as R +-- | A random monad that works with any generator satisfying `RandomGen`. newtype MRand g a = MRand (g -> (a, g)) instance R.RandomGen g => Monad (MRand g) where@@ -14,21 +15,31 @@ instance R.RandomGen g => Monad (MRand g) where MRand m1 = f a in m1 g') +-- | Evaluate an instance of the `MRand` monad with a source of randomness. eval :: R.RandomGen g => MRand g a -> g -> a eval (MRand f) g = fst (f g) +-- | Provides a random value. random :: (R.RandomGen g, R.Random a) => MRand g a random = MRand R.random -randomR :: (R.RandomGen g, R.Random a) => (a, a) -> MRand g a+-- | Provides a random value in a range.+randomR :: (R.RandomGen g, R.Random a)+ => (a, a) -- ^ An inclusive range [lo, hi].+ -> MRand g a randomR r = MRand (R.randomR r) +-- | Provides an infinite list of random values. randoms :: (R.RandomGen g, R.Random a) => MRand g [a] randoms = MRand (\g -> let (g', g'') = R.split g in (R.randoms g', g'')) -randomRs :: (R.RandomGen g, R.Random a) => (a, a) -> MRand g [a]+-- | Provides an infinite list of random values in a range.+randomRs :: (R.RandomGen g, R.Random a)+ => (a, a) -- ^ An inclusive range [lo, hi].+ -> MRand g [a] randomRs r = MRand (\g -> let (g', g'') = R.split g in (R.randomRs r g', g'')) +-- Demonstrates two infinite, independent sources of integers. two :: R.RandomGen g => MRand g ([Int], [Int]) two = do xs <- randomRs (1, 10)@@ -40,4 +51,3 @@ main = do let (xs, ys) = eval two g print $ take 10 xs print $ take 10 ys-
@@ -4,16 +4,13 @@ module QC.Heap ) where -import Control.Monad (liftM)-import Data.List (sort) import Data.Monoid (Monoid, mappend, mempty)-import Test.QuickCheck import Text.Printf (printf) +-- | A boring functional min-heap. data Heap a = Nil | Node { left :: Heap a, right :: Heap a, value :: a } deriving Eq- -- a min-heap instance Ord a => Monoid (Heap a) where mempty = empty@@ -28,21 +25,16 @@ instance Show a => Show (Heap a) where indent d = concat $ replicate d " " empty :: Ord a => Heap a-isEmpty :: Ord a => Heap a -> Bool-single :: Ord a => a -> Heap a-insert :: Ord a => Heap a -> a -> Heap a-merge :: Ord a => Heap a -> Heap a -> Heap a-peek :: Ord a => Heap a -> a-delete :: Ord a => Heap a -> Heap a-heapify :: Ord a => [a] -> Heap a- empty = Nil +isEmpty :: Ord a => Heap a -> Bool isEmpty Nil = True isEmpty _ = False +single :: Ord a => a -> Heap a single = Node Nil Nil +insert :: Ord a => Heap a -> a -> Heap a insert Nil x = single x insert n@(Node l r v) x = if x <= v then Node n Nil x else@@ -53,64 +45,17 @@ insert n@(Node l r v) x = (Node _ _ lv, Node _ _ rv) -> if lv <= rv then Node (insert l x) r v else Node l (insert r x) v -merge h Nil = h-merge h (Node l r v) = insert (merge (merge h l) r) v-+peek :: Ord a => Heap a -> a peek Nil = error "cannot peek empty heap" peek (Node _ _ v) = v +delete :: Ord a => Heap a -> Heap a delete Nil = Nil delete (Node l r v) = merge l r +heapify :: Ord a => [a] -> Heap a heapify = foldl insert empty ---- random testing--instance (Arbitrary a, Ord a) => Arbitrary (Heap a) where- arbitrary = liftM heapify arbitrary-- shrink Nil = []- shrink h = [delete h]---- peek (insert empty x) == x-prop_single :: Int -> Bool-prop_single x = peek (insert empty x) == x---- peek (heapify xs) == minimum xs-prop_heapify :: [Int] -> Property-prop_heapify xs = not (null xs) ==> peek (heapify xs) == minimum xs---- peek (delete (heapify xs)) == head (tail (sort xs))-prop_delete :: [Int] -> Property-prop_delete xs =- length xs >= 2 ==> peek (delete (heapify xs)) == head (tail (sort xs))---- peek (foldl merge empty heaps) == minimum (map peek heaps)-prop_merge :: [Heap Int] -> Property-prop_merge heaps =- not (null heaps) && all (not . isEmpty) heaps- ==> peek (foldl merge empty heaps) == minimum (map peek heaps)---- forall n . n <= left n && n <= right n-prop_heap :: Heap Int -> Bool-prop_heap Nil = True-prop_heap (Node Nil Nil _) = True-prop_heap (Node (Node _ _ lv) Nil v) = v <= lv-prop_heap (Node Nil (Node _ _ rv) v) = v <= rv-prop_heap (Node (Node _ _ lv) (Node _ _ rv) v) = v <= lv && v <= rv--tests :: [(String, Property)]-tests = [ ("peek (insert empty x) == x", property prop_single)- , ("peek (heapify xs) == minimum xs", property prop_heapify)- , ( "peek (delete (heapify xs)) == head (tail (sort xs))"- , property prop_delete- )- , ( "peek (foldl merge empty heaps) == minimum (map peek heaps)"- , property prop_merge- )- , ("forall n . n <= left n && n <= right n", property prop_heap)- ]--main :: IO ()-main = mapM_ (\(s, p) -> do { putStrLn s; quickCheck p}) tests+merge :: Ord a => Heap a -> Heap a -> Heap a+merge h Nil = h+merge h (Node l r v) = insert (merge (merge h l) r) v
@@ -0,0 +1,56 @@+module QC.HeapProps where++import Data.List (sort)+import Control.Monad (liftM)+import Test.QuickCheck++-- import QC.Heap +import QC.SkewHeap++instance (Arbitrary a, Ord a) => Arbitrary (Heap a) where+ arbitrary = liftM heapify arbitrary++ shrink Nil = []+ shrink h = [delete h]++-- peek (insert empty x) == x+prop_single :: Int -> Bool+prop_single x = peek (insert empty x) == x++-- peek (heapify xs) == minimum xs+prop_heapify :: [Int] -> Property+prop_heapify xs = not (null xs) ==> peek (heapify xs) == minimum xs++-- peek (delete (heapify xs)) == head (tail (sort xs))+prop_delete :: [Int] -> Property+prop_delete xs =+ length xs >= 2 ==> peek (delete (heapify xs)) == head (tail (sort xs))++-- peek (foldl merge empty heaps) == minimum (map peek heaps)+prop_merge :: [Heap Int] -> Property+prop_merge heaps =+ not (null heaps) && all (not . isEmpty) heaps+ ==> peek (foldl merge empty heaps) == minimum (map peek heaps)++-- forall n . n <= left n && n <= right n+prop_heap :: Heap Int -> Bool+prop_heap Nil = True+prop_heap (Node Nil Nil _) = True+prop_heap (Node (Node _ _ lv) Nil v) = v <= lv+prop_heap (Node Nil (Node _ _ rv) v) = v <= rv+prop_heap (Node (Node _ _ lv) (Node _ _ rv) v) = v <= lv && v <= rv++tests :: [(String, Property)]+tests = [ ("peek (insert empty x) == x", property prop_single)+ , ("peek (heapify xs) == minimum xs", property prop_heapify)+ , ( "peek (delete (heapify xs)) == head (tail (sort xs))"+ , property prop_delete+ )+ , ( "peek (foldl merge empty heaps) == minimum (map peek heaps)"+ , property prop_merge+ )+ , ("forall n . n <= left n && n <= right n", property prop_heap)+ ]++main :: IO ()+main = mapM_ (\(s, p) -> do { putStrLn s; quickCheck p}) tests
@@ -34,9 +34,9 @@ put (Q heap clock) x = Q (H.insert heap (Clocked clock x)) (clock + 1) get = value . H.peek . elems rest (Q heap clock) = Q (H.delete heap) clock -instance (Arbitrary a, Ord a) => Arbitrary (Q a) where- arbitrary = liftM2 Q arbitrary arbitrary--instance Arbitrary a => Arbitrary (Clocked a) where- arbitrary = liftM2 Clocked arbitrary arbitrary+-- instance (Arbitrary a, Ord a) => Arbitrary (Q a) where + -- arbitrary = liftM2 Q arbitrary arbitrary +-- +-- instance Arbitrary a => Arbitrary (Clocked a) where + -- arbitrary = liftM2 Clocked arbitrary arbitrary
@@ -6,6 +6,7 @@ import Control.Monad (liftM2) import Test.QuickCheck import Text.Printf (printf) +-- | A queue with O(1) `put` and amortized O(1) `get` operations. data Q a = Q { front :: [a], back :: [a] } -- corresponds to front ++ reverse back @@ -13,39 +14,50 @@ instance Show a => Show (Q a) where show (Q xs ys) = printf "(F: %s, B: %s)" (show xs) (show ys) empty :: Q a-isEmpty :: Q a -> Bool-put :: Q a -> a -> Q a-get :: Q a -> a -- defined on nonempty queue only-rest :: Q a -> Q a -- defined on nonempty queue only- empty = Q [] [] +isEmpty :: Q a -> Bool isEmpty q = null (front q) && null (back q) --- put q a = Q [] (a : back q) +-- | Adds an element to the back of the queue.+put :: Q a -> a -> Q a put q a = Q (front q) (a : back q)+-- put q a = Q [] (a : back q) -- nr's error #1 +-- | Returns an element from the top of the queue.+-- Calling @get@ on an empty queue results in a runtime error.+get :: Q a -> a -- defined on nonempty queue only get (Q [] []) = error "get from empty queue" get (Q [] as) = get (Q (reverse as) []) get (Q (a:as) _) = a +-- | Returns the given queue with the top of the queue removed.+-- Calling @rest@ on an empty queue results in a runtime error.+rest :: Q a -> Q a -- defined on nonempty queue only rest (Q [] []) = error "rest from empty queue" rest (Q [] as) = rest (Q (reverse as) []) rest (Q (a:as) bs) = Q as bs--- rest (Q (a:as) _) = Q as [] +-- rest (Q (a:as) _) = Q as [] -- nr's error #2 -- random testing +-- | QTerms represents a series of *valid* combinations of queue constructors. data QTerms = Empty | Rest QTerms | Put QTerms deriving Show --- max # of queue terms -> all valid combos -enumerate :: Int -> [QTerms]+-- | Enumerates all valid queues up to a certain number of constructor+-- applications.+enumerate :: Int -- ^ Maximum number of queue terms to enumerate.+ -> [QTerms] -- ^ All valid permutations of queue constructors. enumerate = e [] Empty 0- where e :: [QTerms] -> QTerms -> Int -> Int -> [QTerms]+ where e :: [QTerms] -- ^ Accumulated permutations.+ -> QTerms -- ^ Initial set of queue terms.+ -> Int -- ^ Number of items in the queue.+ -> Int -- ^ Number of queue terms left to generate.+ -> [QTerms] e perms _ _ 0 = perms e perms ts 0 n = e (next:perms) next 1 (n-1) where next = Put ts@@ -56,21 +68,28 @@ enumerate = e [] Empty 0 instance Arbitrary QTerms where arbitrary = elements $ enumerate 15 + -- Removing the outer most constructor application always results+ -- in a valid queue. shrink Empty = [] shrink (Rest ts) = [ts] shrink (Put ts) = [ts] +-- | Constructs a queue given a list of values and a construction of+-- a queue via `QTerms`. asQ :: [a] -> QTerms -> Q a asQ _ Empty = empty asQ as (Rest ts) = rest $ asQ as ts asQ (a:as) (Put ts) = put (asQ as ts) a +-- | Convenience function for building a queue of integers. intQ :: QTerms -> Q Int intQ = asQ [1..] +-- | Returns the last item in a queue. lastItem :: Ord a => Q a -> a lastItem q = if isEmpty (rest q) then get q else lastItem (rest q) +-- | Drains all of the items in the queue and returns the empty queue. drain :: Ord a => Q a -> Q a drain q | isEmpty q = empty
QC/QueueProps.hs
@@ -1,47 +0,0 @@-module QC.QueueProps where--import Test.QuickCheck (Property, property, quickCheck)---- import Q -import QC.PQueue--lastItem :: Ord a => Q a -> a-lastItem q = if isEmpty (rest q) then get q else lastItem (rest q)--drain :: Ord a => Q a -> Q a-drain q- | isEmpty q = empty- | otherwise = drain (rest q)---- get (put empty x) == x-prop_empty :: Int -> Bool-prop_empty x = get (put empty x) == x---- lastItem (put queue x) == x--- This is not true for priority queues!-prop_last :: Q Int -> Int -> Bool-prop_last q x = lastItem (put q x) == x---- isEmpty (drain q) == true-prop_drainable :: Q Int -> Bool-prop_drainable q = isEmpty (drain q)---- get (rest (put (put empty x) y)) == y-prop_puts :: (Int, Int) -> Bool-prop_puts (x, y) = get (rest (put (put empty x) y)) == y---- get (put (rest (put (put empty x) y)) z) == y-prop_restPuts :: (Int, Int, Int) -> Bool-prop_restPuts (x, y, z) = get (put (rest (put (put empty x) y)) z) == y--tests :: [(String, Property)]-tests = [ ("empty", property prop_empty)- , ("last", property prop_last)- , ("drainable", property prop_drainable)- , ("puts", property prop_puts)- , ("restPuts", property prop_restPuts)- ]--main :: IO ()-main = mapM_ (\(s, p) -> do { putStrLn s; quickCheck p}) tests-
@@ -4,16 +4,13 @@ module QC.SkewHeap ) where -import Control.Monad (liftM)-import Data.List (sort) import Data.Monoid (Monoid, mappend, mempty)-import Test.QuickCheck import Text.Printf (printf) +-- | A skew min-heap. data Heap a = Nil | Node { left :: Heap a, right :: Heap a, value :: a } deriving Eq- -- a min-heap instance Ord a => Monoid (Heap a) where mempty = empty@@ -28,85 +25,33 @@ instance Show a => Show (Heap a) where indent d = concat $ replicate d " " empty :: Ord a => Heap a-isEmpty :: Ord a => Heap a -> Bool-single :: Ord a => a -> Heap a-insert :: Ord a => Heap a -> a -> Heap a-merge :: Ord a => Heap a -> Heap a -> Heap a-peek :: Ord a => Heap a -> a-delete :: Ord a => Heap a -> Heap a-heapify :: Ord a => [a] -> Heap a- empty = Nil +isEmpty :: Ord a => Heap a -> Bool isEmpty Nil = True isEmpty _ = False +single :: Ord a => a -> Heap a single = Node Nil Nil +insert :: Ord a => Heap a -> a -> Heap a insert h = merge h . single +peek :: Ord a => Heap a -> a peek Nil = error "cannot peek empty heap" peek (Node _ _ v) = v +delete :: Ord a => Heap a -> Heap a delete Nil = Nil delete (Node l r v) = merge l r +heapify :: Ord a => [a] -> Heap a heapify = foldl insert empty -- | Implements the alternating left/right subtree merge which makes -- this a skew tree.+merge :: Ord a => Heap a -> Heap a -> Heap a merge Nil h = h merge h Nil = h merge n1 n2 = Node (merge (right s) b) (left s) (value s) where (s, b) = if value n1 <= value n2 then (n1, n2) else (n2, n1)----- random testing--instance (Arbitrary a, Ord a) => Arbitrary (Heap a) where- arbitrary = liftM heapify arbitrary-- shrink Nil = []- shrink h = [delete h]---- peek (insert empty x) == x-prop_single :: Int -> Bool-prop_single x = peek (insert empty x) == x---- peek (heapify xs) == minimum xs-prop_heapify :: [Int] -> Property-prop_heapify xs = not (null xs) ==> peek (heapify xs) == minimum xs---- peek (delete (heapify xs)) == head (tail (sort xs))-prop_delete :: [Int] -> Property-prop_delete xs =- length xs >= 2 ==> peek (delete (heapify xs)) == head (tail (sort xs))---- peek (foldl merge empty heaps) == minimum (map peek heaps)-prop_merge :: [Heap Int] -> Property-prop_merge heaps =- not (null heaps) && all (not . isEmpty) heaps- ==> peek (foldl merge empty heaps) == minimum (map peek heaps)---- forall n . n <= left n && n <= right n-prop_heap :: Heap Int -> Bool-prop_heap Nil = True-prop_heap (Node Nil Nil _) = True-prop_heap (Node (Node _ _ lv) Nil v) = v <= lv-prop_heap (Node Nil (Node _ _ rv) v) = v <= rv-prop_heap (Node (Node _ _ lv) (Node _ _ rv) v) = v <= lv && v <= rv--tests :: [(String, Property)]-tests = [ ("peek (insert empty x) == x", property prop_single)- , ("peek (heapify xs) == minimum xs", property prop_heapify)- , ( "peek (delete (heapify xs)) == head (tail (sort xs))"- , property prop_delete- )- , ( "peek (foldl merge empty heaps) == minimum (map peek heaps)"- , property prop_merge- )- , ("forall n . n <= left n && n <= right n", property prop_heap)- ]--main :: IO ()-main = mapM_ (\(s, p) -> do { putStrLn s; quickCheck p}) tests