I have a question about how to manipulate a function in Haskell. I have this graph:
import Data.Map (Map,empty,member,insert)
import Graphviz
-- | A directed graph
data Graph v = Graph
{ arcsMap :: Map v [v] -- A map associating a vertex with its successors
, labelMap :: Map v String -- The Graphviz label of each node
, styleMap :: Map v String -- The Graphviz style of each node
}deriving (Show,Eq, Ord)
And I have those functions
-- | Adds a vertex to a graph
addVertex :: Ord v => v -> Graph v -> Graph v
addVertex _ (Graph arcs labels styles)= (Graph arcs labels styles)
addVertex v (Graph arcs labels styles) = Graph (insert v [] arcs) labels styles
-- | Adds vertices to a graph
addVertices :: Ord v => Graph v -> [v] -> Graph v
addVertices (Graph arcs labels styles) [v] = map addVertex [v]
My problem is that I try to send all the vertices of my list to my addVertex function using map. But I do not know how to write it correctly (maybe I'm completely wrong and the map function is a very bad idea).
This is usually done with a "fold" like foldr :: Foldable f => (a -> b -> b) -> b -> f a -> b. Indeed we can add the elements of a list of vertices (or in fact a Foldable f => f of items):
addVertices :: (Foldable f, Ord v) => Graph v -> f v -> Graph v
addVertices g0 = foldr addVertex g0
For a list, you can see foldr as a way to replace the empty list [] with the base element g0, and we replace all "cons" (:) with the function addVertex here. So that means that for a list:
v1 : v2 : v3 : []
or more verbose:
(:) v1 ((:) v2 ((:) v3 []))
we will calculate the result as:
addVertex v1 (addVertex v2 (addVertex v3 g0))
and thus each time add one vertex to the graph.
We can use foldl :: Foldable f => (b -> a -> b) -> b -> f a -> b instead to pass the accumulator left-to-right:
addVertices :: (Foldable f, Ord v) => Graph v -> f v -> Graph v
addVertices g0 = foldl (flip addVertex) g0
Then we will fold the list as:
addVertex v3 (addVertex v2 (addVertex v1 g0))
Since we use a Foldable here, we can add the vertices in all sorts of data structures, like Maybe v, [v], Tree v, etc.
We can here use an η-reduction, and implement the functions as:
addVertices1 :: (Foldable f, Ord v) => Graph v -> f v -> Graph v
addVertices1 = foldr addVertex
addVertices2 :: (Foldable f, Ord v) => Graph v -> f v -> Graph v
addVertices2 = foldl (flip addVertex)
Related
As pretext: I have, prior to this, written a function that counts the amount of times a pair of words occur in a text, this function calculates every single pair of words throughout the text.
Like so:
pairCounter = map (\x -> (head x,length x)). groupTuples . sort
This function returns: [((String, String), Int)] The first/second string being word1/2 in the pair, and the Int is how many times this can be found, or the "tally" of the pair if you will.
What I now would like to do is create a function that only returns the "neighbors" to any given word. For instance:
neighbours [(("red","car"),2),(("house","red"),1)] "red"
should return [("car",2),("house",1)] or some reordering of this list.
So basically; we have established all pairs of any given word, but now I want to single out only the neighbors to this word and a tally of its frequency.
So far, I have thought about using filters in this way:
filter (\(x, y) -> x /= c || y /= c)
--^(I have no idea if this is syntax correct but it is just to give myself an idea where to start)
However I find it hard to come up with a way to use filters and also include the tally of my neighbors, my Int argument that is.
One very idiomatic way would be via a list comprehension:
neighbours :: Eq a => [((a, a), b)] -> a -> [(a, b)]
neighbours items query =
[ (neighbor, count)
| ((s1, s2), count) <- items
, (self, neighbor) <- [(s1, s2), (s2, s1)]
, self == query
]
Actually, I'd probably put the arguments in the other order to match conventions used in existing libraries and shorten the names so that it comfortably fits on one line:
neighbours :: Eq a => a -> [((a, a), b)] -> [(a, b)]
neighbours x xs = [(x4, n) | ((x1, x2), n) <- xs, (x3, x4) <- [(x1, x2), (x2, x1)], x3 == x]
I suspect that the part where you don't care about order will come up in other parts of your code, and so additionally I would consider splitting out a part that symmetrizes. This will also be helpful if, later, you decide that pairs that occur in both orders should be normalized and their counts summed or some such thing, because you will only have to change one location to propagate that update to all consumers.
-- (s)ource, (t)arget, (u)ndirected edge, (w)eight, (w)eighted edge(s)
undirected :: [((a, a), b)] -> [((a, a), b)]
undirected ws = [(u, w) | ((s, t), w) <- ws, u <- [(s, t), (t, s)]]
neighbours :: Eq a => a -> [((a, a), b)] -> [(a, b)]
neighbours x xs = [(t, w) | ((s, t), w) <- undirected xs, s == x]
Alternately, you might decide to make the graph undirected from the very beginning.
import qualified Data.Map as M
-- export UPair the type but not UPair the constructor
data UPair a = UPair a a
deriving (Eq, Ord, Read, Show, Functor, Foldable, Traversable)
-- this is strict. can also make a lazy variant
upair :: Ord a => a -> a -> UPair a
upair a a' = if a < a' then UPair a a' else UPair a' a
pairCounter :: [a] -> M.Map (UPair a) Int
pairCounter as = M.fromListWith (+) $ zipWith (\a a' -> (upair a a', 1)) as (tail as)
For a given word c you thus should retain the items for which the first String, or the second String are equal to c. We should use ((s1, s2), v) as pattern since the outer 2-tuple has as elements a 2-tuple of Strings as first item, and an Int as second item.
We can work with concatMap :: Foldable t => (a -> [b]) -> t a -> [b] and work with a function that will return [(s2, v)] if s1 matches, [(s1, v)] if s2 matches, and the empty list if none of the two elements was a match:
We thus filter with:
neighbors :: (Foldable f, Eq a) -> f ((a, a), b) -> a -> [(a, b)]
neighbors items query = concatMap f items
where f ((s1, s2), v)
| query == s1 = [(s2, v)]
| query == s2 = [(s1, v)]
| otherwise = []
I'm trying to understand how to handle calls error in Haskell. For example:
I have this graph
import Data.Map (Map,empty,member,insert,keys,(!))
import Graphviz
-- | A directed graph
data Graph v = Graph
{ arcsMap :: Map v [v] -- A map associating a vertex with its successors
, labelMap :: Map v String -- The Graphviz label of each node
, styleMap :: Map v String -- The Graphviz style of each node
}deriving (Show,Eq, Ord)
And I have the function
-- | Returns the successors of a vertex in a graph in ascending order
--
-- We say that `v` is a successor of `u` in a graph `G` if the arc `(u,v)`
-- belongs to `G`.
--
-- Note: Returns the empty list if the vertex does not belong to the graph.
--
-- >>> successors 1 emptyGraph
-- []
-- >>> successors 1 $ addArc (1,2) emptyGraph
-- [2]
-- >>> successors 1 $ addArcs emptyGraph [(1,2),(2,3),(1,3)]
-- [2,3]
successors :: Ord v => v -> Graph v -> [v]
successors v (Graph arcs labels styles) = arcs ! v
The documentation of the operator (!) says : "O(log n). Find the value at a key. Calls error when the element cannot be found."
I want to return an empty list [] when the element can't be found
How should I handle this ?
Thank you very much.
You can make use of the !? operator (Ord k => Map k a -> k -> Maybe a) here. This will return a Nothing in case the key does not exist, and Just v with v the value in case that exists, as specified by the documentation:
O(log n). Find the value at a key. Returns Nothing when the element can not be found.
Then we still need to convert that to an empty list. We can use fromMaybe :: a -> Maybe a -> a for that, here we first give a "default" value to use in case of a Nothing, and then we pass it a Maybe a. In case it is a Nothing, we use the default, in case it is a Just v, it will return v:
import Data.Map (Map,empty,member,insert,keys,(!?))
import Data.Maybe(fromMaybe)
successors :: Ord v => v -> Graph v -> [v]
successors v (Graph arcs _ _) = fromMaybe [] (arcs !? v)
or as #Bergi says, we can use findWithDefault :: Ord k => a -> k -> Map k a -> a:
import Data.Map (Map,empty,member,insert,keys,findWithDefault)
import Data.Maybe(fromMaybe)
successors :: Ord v => v -> Graph v -> [v]
successors v (Graph arcs _ _) = findWithDefault [] v arcs
for an exercise I need to reverse a graph (reverse all edges), but I don't get anywhere.
So I need some help.
I am aware you might not want to solve the exercise for me, so that's not what I am asking for. I just need to get some advice...
So to get to it:
data Graph a = G
{ nodes :: [a]
, successors :: a -> [a] }
reverseGraph :: Eq a => Graph a -> Graph a
A graph has to parameters: a list of nodes and a function that defines the successors. This function has the type:
a -> [a]
for example:
graph1 :: Graph Int
graph1 = G [1..6] $ \case 1 -> [2,3]
2 -> []
3 -> [1,4,6]
4 -> [1]
5 -> [3,5]
6 -> [2,4,5]
the reversed graph would be:
reverseGraph graph1 ~>
2 -> [1,6]
3 -> [1,5]
1 -> [3,4]
4 -> [3,6]
6 -> [3]
5 -> [5,6]
I get that I need to check for each node in the input graph the successors and add for each the input node to the new successor list of the output node.
But i just don't get how to do this in Haskell.
Any help is appreciated!
Here is my solution for anyone who may attempt something similar:
reverseGraph :: Eq a => Graph a -> Graph a
reverseGraph (G nodes sucs) = (G nodes sucs') where
sucs' a = getVert a nodes sucs
--Makes a list of all occurrences of v in the succeccor list.
getVert :: Eq a => a -> [a] -> (a-> [a]) -> [a]
getVert v [] succs = []
getVert v (n:ns) succs = if v `elem` succs n then [n]++getVert v ns succs else getVert v ns succs
Here's a hint. Let's consider the reverse of G vertices edges.
That will be of the form G vertices' edges'.
It's obvious that vertices' = vertices.
What about edges'? Well, for any value v, edges' v must return
"the list of all the w in vertices such that edge w contains v as an element"
You can translate the above English description into Haskell code using a list comprehension. You can use x `elem` list to check whether x is an element of list.
module Graph where
import Control.Monad.State
import Data.Maybe
import Data.Set as Set
-- | 'Edge' represents an edge entre two nodes.
data Edge v = Edge {source :: v, target :: v}
deriving (Show,Eq,Ord)
data Graph v = Graph {nodes :: Set v, edges :: Set (Edge v)}
deriving Show
-- | 'Eq' for graphs.
instance Ord v => Eq (Graph v) where
g == g' = nodes g == nodes g' && edges g == edges g'
isValid :: Ord v => Graph v -> Bool
isValid g = (Set.map source (edges g) `Set.union` Set.map target (edges g)) `isSubsetOf` nodes g
-- | 'isDAG' tests if a graph is acyclic
isDAG :: Ord v => Graph v -> Bool
isDAG g = isValid g && all nocycle (nodes g)
where nocycle v = all (\a -> v `notMember` reachable g a) $ Set.map target (adj g v)
I'm trying to use QuickCheck to test 'isDAG', could you give me an property that 'isDAG' abides?
prop_isDAG :: Graph Int -> Property
prop_isDAG g =
I believe that's the accurate function declaration, if not, feel free to change it!
Here is another example that I've made for another function, to guide possible helpers on what i'm looking for:
-- | The function 'isSubgraphOf' tests if the first graph is subgraph of the second.
isSubgraphOf :: Ord v => Graph v -> Graph v -> Bool
isSubgraphOf g g' = isSubsetOf (nodes g) (nodes g') && isSubsetOf (edges g) (edges g')
And this is the QuickCheck property that it abides:
-- QuickCheck proprety to test 'isSubgraphOf'
prop_isSubgraphOf :: Graph Int -> Property
prop_isSubgraphOf g = forAll (elements $ elems $ nodes g) $ \v -> adj g v `isSubsetOf` edges g
prop_dag :: Property
prop_dag = forAll (dag :: Gen (DAG Int)) $ \g -> collect (length (edges g)) $ isDAG g
This was what I was looking for.
In Haskell's Functional Graph Library (FGL), most of the graph algorithms depend on the 'match' function, which, given a Node n and a Graph g, returns c & g', where c is the Context of the n, and g' is the rest of the graph (which contains no references to n).
The only way I can see of doing this is be examining each of the contexts in g and removing any edges which refer to n and adding them to the context c. This would take linear time, I believe.
Martin Erwig, who wrote the library, suggests in this paper that this transformation can be done in constant or at least sub-linear time. Can anyone explain to me how this is accomplished?
match is defined in the Graph typeclass, so the implementation of that function depends on the datatype that implements the typeclass.
The package comes with two implementations, one using Patricia trees, one using regular trees. You can view the source for either yourself.
For example, the Patricia tree implementation:
import Data.Graph.Inductive.Graph
import Data.IntMap (IntMap)
import qualified Data.IntMap as IM
import Data.List
import Data.Maybe
import Control.Arrow(second)
newtype Gr a b = Gr (GraphRep a b)
type GraphRep a b = IntMap (Context' a b)
type Context' a b = (IntMap [b], a, IntMap [b])
type UGr = Gr () ()
instance Graph Gr where
-- ...
match = matchGr
-- ...
matchGr :: Node -> Gr a b -> Decomp Gr a b
matchGr node (Gr g)
= case IM.lookup node g of
Nothing
-> (Nothing, Gr g)
Just (p, label, s)
-> let !g1 = IM.delete node g
!p' = IM.delete node p
!s' = IM.delete node s
!g2 = clearPred g1 node (IM.keys s')
!g3 = clearSucc g2 node (IM.keys p')
in
(Just (toAdj p', node, label, toAdj s), Gr g3)
lookup and delete on IntMaps have O(min(n,W)) runtime, which is effectively constant on a given machine with a set integer width (W).
So that just leaves clearPred, clearSucc, and toAdj:
clearSucc :: GraphRep a b -> Node -> [Node] -> GraphRep a b
clearSucc g _ [] = g
clearSucc g v (p:rest) = clearSucc g' v rest
where
g' = IM.adjust f p g
f (ps, l, ss) = (ps, l, IM.delete v ss)
clearPred :: GraphRep a b -> Node -> [Node] -> GraphRep a b
clearPred g _ [] = g
clearPred g v (s:rest) = clearPred g' v rest
where
g' = IM.adjust f s g
f (ps, l, ss) = (IM.delete v ps, l, ss)
adjust is also O(min(n,W)), so we don't need to worry about that. Both clearSucc and clearPred recurse through each element in the adjacency list, though, so that's O(degree) combined.
toAdj :: IntMap [b] -> Adj b
toAdj = concatMap expand . IM.toList
where
expand (n,ls) = map (flip (,) n) ls
toAdj creates a new edge list, which is O(max(|V|,|E|)), but this is constructed lazily, so we don't need to worry about this unless it's used.