Last active
December 9, 2015 09:02
-
-
Save slawo-ch/25653b1466aa070dcda3 to your computer and use it in GitHub Desktop.
Topological sort in clojure (Tarjan's depth first algorighm)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (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))) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (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]} |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See: https://en.wikipedia.org/wiki/Topological_sorting#Tarjan.27s_algorithm