I'm writing a Clojure function to perform a topological sort via depth-first search on a directed graph, and for some inputs it doesn't terminate. It uses loop-recur, but I don't see any lazy sequences used in arguments to recur, which seems to be the most common culprit for infinite loops. I ran the program on paper for the sample inputs below and they all appeared to work fine.
(require '[clojure.set :as set])
;graph is a hash map, keys are nodes, vals are
;collections of other nodes that node points to
(defn DFSsort [graph]
(loop [stack `(~(ffirst graph)),
visited '()]
(let [unvisited (set/difference (set (keys graph))
(set visited)),
node (peek stack),
neigh (graph node)]
(if (empty? stack)
(if (seq unvisited)
(recur (conj stack (first unvisited))
visited)
visited) ; return
(if (seq (set/difference (set neigh) (set visited)))
(if (not-any? (partial = (first neigh)) stack)
(recur (conj stack (first neigh))
visited)
"Cycle detected!") ; abort
(recur (pop stack)
(conj visited node)))))))
(DFSsort {1 [2 3], 2 [3], 3 []})
;=> (1 2 3)
(DFSsort {1 [2 3], 2 [], 3 []})
;Infinite loop
(DFSsort {1 [2 3], 2 [], 3 [2]})
;Infinite loop