Introducing
Your new presentation assistant.
Refine, enhance, and tailor your content, source relevant images, and edit visuals quicker than ever before.
Trending searches
Berlin Haskell Meetup 2017-11-15
Graphs are a fundamental data structure in computer science because a lot of problems can be modelled using graphs…
…can I use a functional programming language to solve those problems efficiently & elegantly?
Image taken from a slide of the Algorithms part 2 MOOC on Coursera by Bob Sedgwick and Kevin Wayne
* Too verbose to show a code snippet in such a small available space
type Forest a = [RoseTree a]
type Vertex = Int
type EdgeNode = (Vertex, Weight)
type Graph = Immutable.Array Vertex [EdgeNode]
dfs :: Graph -> [Vertex] -> Forest Vertex
dfs g = prune (bounds g) . map (generate g)
where
prune bnds ts = runST $ do
array <- mkEmpty bnds
chop ts array
chop :: Forest Vertex -> Mutable.Array Vertex Bool -> Forest Vertex
This code is a simplified version of the real one and does not type-check
chop :: (MA.MArray (MA.STUArray s) Bool m)
=> Forest Vertex
-> MA.STUArray s Vertex Bool
-> m (Forest Vertex)
chop [] _arr = return []
chop (Node v ts:ns) arr = do
visited <- contains arr v
if visited then chop ns arr -- prune ts
else do
include arr v
-- traverse left-to-right
ts' <- chop ts arr
-- traverse top-to-bottom
ns' <- chop ns arr
return $ Node v ts' : ns'
data Graph w l = Empty
| Context w l :&: Graph w l
type Context w l = (Adj w, Vertex, l, Adj w)
type Adj w = [(w, Vertex)]
type Vertex = Int
\: read "mkG [('a', 1), ('b', 2), ('c', 3)] [(1, 2, 5), (2, 1, 3), (2, 3, 1), (3, 1, 4)]"
([],3,'c',[(4,'a')]) :&: (([(5,'a')],2,'b',[(3,'a'),(1,'c')]) :&: (([],1,'a',[]) :&: Empty))
\: read "mkG [('c', 3), ('b', 2), ('a', 1)] [(1, 2, 5), (2, 1, 3), (2, 3, 1), (3, 1, 4)]"
([(4,'c'),(3,'b')],1,'a',[(5,'b')]) :&: (([],2,'b',[(1,'c')]) :&: (([],3,'c',[]) :&: Empty))
An extensions of patterns that allows to apply a function to the argument before the matching happens.
c (:&: ! v) g
The code above won't type-check! It's only meant to provide an intuition of active patterns
(conceptually) transform the input graph g into one in which the context for the vertex v is inserted last (if v is contained in the graph)
Active patterns are not essential to the implementation of inductive graphs. A function match with the following type signature will also work:
match :: Vertex -> Graph w l -> Maybe (Context w l, Graph w l)
Returns the context for the input vertex (if any) and a new inductive graph that doesn't contain the input vertex. the "shape" of the output graph doesn't matter!
\: let g = read "mkG [('a', 1), ('b', 2), ('c', 3)] [(1, 2, 5), (2, 1, 3), (2, 3, 1), (3, 1, 4)]"
([],3,'c',[(4,'a']) :&: (([(5,'a')],2,'b',[(3,'a'),(1,'c')]) :&: (([],1,'a',[]) :&: Empty))
'a'`match`g == Just (([(4, 'a'),(3,'b')], 1, 'a', [(5,'b')]),g')
Caveat on time complexity of the algorithms on inductive graphs:
assume that :&: and active graph patterns implementations take constant time
in a nutshell: visit successors before siblings
dfs :: [Vertex] -> Graph w l -> [Vertex]
dfs _ Empty = []
dfs [] _ = []
dfs (v:vs) g = maybe (dfs vs g) dfs' (v `match` g)
where
-- `succs` returns all the successor edges
dfs' (ctx@(_,vtx,_,_), g') = vtx : dfs ((succs ctx) ++ vs) g'
in a nutshell: visit successors after siblings
bfs :: [Vertex] -> Graph w l -> [Vertex]
bfs _ Empty = []
bfs [] _ = []
bfs (v:vs) g = maybe (bfs vs g) bfs' (v `match` g)
where
-- `succs` returns all the successor edges
bfs' (ctx@(_,vtx,_,_), g') = vtx : bfs (vs ++ (succs ctx)) g'
Breadth-first search
newtype LPath l = LPath { unwrap :: [(l, Vertex)] }
instance Eq l => Eq (LPath l) where …
instance Ord l => Ord (LPath l) where …
type LRTree l = [LPath l] -- Labelled R-Tree (or Root Tree)
…
dijkstra :: (Monoid w, Ord w) => Heap.Heap (LPath w) -> Graph w l -> LRTree w
dijkstra h g | isEmpty g = []
| otherwise = maybe [] dijkstra' (Heap.viewMin h)
where
dijkstra' (LPath p@((_, v):_), h') =
maybe (dijkstra h' g) (\(ctx, g') -> LPath p : dijkstra (mergeAll p h' ctx) g')
(v `match` g)
mergeAll :: [(l, Vertex)] -> Heap.Heap (LPath l) -> Context l l -> Heap.Heap (LPath l)
shortestPathTree :: (Monoid w, Ord w) => Vertex -> Graph w l -> LRTree w
shortestPathTree src = dijkstra (Heap.singleton $ LPath [(mempty, src)])
shortestPath :: (Monoid w, Ord w) => Vertex -> Vertex -> Graph w l -> Path
shortestPath src dst = getPath dst . shortestPathTree src
Yours truly