This is a continuation of my previous question about processing a vector representation of an edge-oriented 5.1m graph. I am trying to implement the Kosaraj graph algorithm and, therefore, I need to rearrange my Vector in the order of the end time of the first depth search (DFS) at the edges. I have code that works on small data sets, but it cannot return after 10 minutes to the full data set. (I cannot exclude that the cycle arises from a large graph, but there are no signs of this in my test data.)
DFS needs to be avoided to revise nodes, so I need some kind of “state” to search (currently a tuple, should I use State Monad?). The first search should return an ordered vector, but for now, I keep simple things by returning a list of Node reordered indices so that I can subsequently process the vector at a time.
I assume the problem is dfsInner . The code below "remembers" the visited nodes, updating the scout field of each Node (third defender). Although I tried to make it tail recursive, the code seems to use memory quickly. Do I need to be forced to some rigor, and if so, how? (I have another version that I use in one search that checks previous visits by looking at the starting nodes of unexplored edges on the stack and a list of nodes that have been completed. It does not grow so fast, but it doesn’t return for a well connected node.)
However, it could also be foldr' , but how can I detect this?
This is presumably Kursers' homework, but I'm not sure I can tag the honor code button! Learning is more important though, so I really don't want the copy / paste answer. What I have is not very elegant - he has an imperative feeling for him, which is caused by the problem of maintaining a kind of state - see The Third Guard. I would welcome comments on design patterns.
type NodeName = Int type Edges = [NodeName] type Explored = Bool type Stack = [(Int, Int)] data Node = Node NodeName Explored Edges Edges deriving (Eq, Show) type Graph = Vector Node main = do edges <- V.fromList `fmap` getEdges "SCC.txt" let maxIndex = fst $ V.last edges gr = createGraph maxIndex edges res = dfsOuter gr --return gr putStrLn $ show res dfsOuter gr = let tmp = V.foldr' callInner (gr,[]) gr in snd tmp callInner :: Node -> (Graph, Stack) -> (Graph, Stack) callInner (Node idx _ fwd bwd) (gr,acc) = let (Node _ explored _ _) = gr V.! idx in case explored of True -> (gr, acc) False -> let initialStack = map (\l -> (idx, l)) bwd gr' = gr V.// [(idx, Node idx True fwd bwd)] (gr'', newScc) = dfsInner idx initialStack (length acc) (gr', []) in (gr'', newScc++acc) dfsInner :: NodeName -> Stack -> Int -> (Graph, [(Int, Int)]) -> (Graph, [(Int, Int)]) dfsInner start [] finishCounter (gr, acc) = (gr, (start, finishCounter):acc) dfsInner start stack finishCounter (gr, acc) | nextStart /= start = -- no more places to go from this node dfsInner nextStart stack (finishCounter + 1) $ (gr, (start, finishCounter):acc) | nextExplored = -- nextExplored || any (\(y,_) -> y == stack0Head) stack || any (\(x,_) -> x == stack0Head) acc = dfsInner start (tail stack) finishCounter (gr, acc) | otherwise = dfsInner nextEnd (add2Stack++stack) finishCounter (gr V.// [(nextEnd, Node idx True nextLHS nextRHS)], acc) -- dfsInner gr stack0Head (add2Stack++stack) finishCounter acc where (nextStart, nextEnd) = head stack (Node idx nextExplored nextLHS nextRHS) = gr V.! nextEnd add2Stack = map (\l -> (nextEnd, l)) nextRHS