Skip to content

Instantly share code, notes, and snippets.

@slawo-ch
Last active December 9, 2015 09:02
Show Gist options
  • Select an option

  • Save slawo-ch/25653b1466aa070dcda3 to your computer and use it in GitHub Desktop.

Select an option

Save slawo-ch/25653b1466aa070dcda3 to your computer and use it in GitHub Desktop.
Topological sort in clojure (Tarjan's depth first algorighm)
(ns twineworks.util.graph)
(defn ^:private toposort-visit [n g state]
(let [{:keys [unmarked processing processed]} state]
(when (contains? processing n)
(throw (ex-info "cannot toposort a cyclic graph" {:cause :cyclic-graph
:cycle-node n
:state state
:graph g})))
(if (contains? processed n)
;; n has already been processed
state
;; n needs processing
(let [children (get g n)
init-state (merge state {:unmarked (disj unmarked n)
:processing (conj processing n)})
post-state (reduce (fn [s c] (toposort-visit c g s)) init-state children)
{:keys [processing processed ordered]} post-state]
(merge post-state {:processed (conj processed n)
:processing (disj processing n)
:ordered (conj ordered n)})))))
(defn toposort
"Topological sort of graph g.
Graph g is represented is a map of nodes to a set of connected nodes.
g = {n1 #{n2 n3}
n2 #{}
n3 #{n4}
n4 #{n2}}
Returns a vector of nodes in topological sort order (items with no dependencies first).
Throws if the graph has cycles."
[g]
(loop [unmarked (set (keys g))
processing #{}
processed #{}
ordered []]
(if (seq unmarked)
; visit any node depth first
(let [visit (toposort-visit (first unmarked)
g
{:unmarked unmarked
:processing processing
:processed processed
:ordered ordered})]
(recur (:unmarked visit)
(:processing visit)
(:processed visit)
(:ordered visit)))
; else -> no more unmarked ones, done
ordered)))
(def empty-graph {})
(graph/toposort empty-graph)
=> []
(def simple-graph {:a #{:b}
:b #{}})
(graph/toposort simple-graph)
=> [:b :a]
(def basic-graph {:a #{:b :c}
:b #{:d}
:c #{:d :e :b}
:d #{}
:e #{:d}})
(graph/toposort basic-graph)
=> [:d :e :b :c :a]
(def cyclic-graph {:a #{:b :c}
:b #{:d}
:c #{:d :e :b}
:d #{}
:e #{:d :a}})
(graph/toposort cyclic-graph)
clojure.core/eval core.clj: 3081
...
twineworks.util.graph/toposort-visit/fn graph.clj: 18
twineworks.util.graph/toposort-visit graph.clj: 6
clojure.core/ex-info core.clj: 4593
clojure.lang.ExceptionInfo: cannot toposort a cyclic graph
cause: :cyclic-graph
cycle-node: :e
graph: {:a #{:c :b}, :b #{:d}, :c #{:e :b :d}, :d #{}, :e #{:d :a}}
state: {:unmarked #{:b},
:processing #{:e :c :a},
:processed #{:d},
:ordered [:d]}
@slawo-ch

slawo-ch commented Dec 9, 2015

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment