module TSort
TSort implements topological sorting using Tarjanâs algorithm for strongly connected components.
TSort is designed to be able to be used with any object which can be interpreted as a directed graph.
TSort requires two methods to interpret an object as a graph, tsort_each_node and tsort_each_child.
-
tsort_each_nodeis used to iterate for all nodes over a graph. -
tsort_each_childis used to iterate for child nodes of a given node.
The equality of nodes are defined by eql? and hash since TSort uses Hash internally.
A Simple Example
The following example demonstrates how to mix the TSort module into an existing class (in this case, Hash). Here, weâre treating each key in the hash as a node in the graph, and so we simply alias the required tsort_each_node method to Hashâs #each_key method. For each key in the hash, the associated value is an array of the nodeâs child nodes. This choice in turn leads to our implementation of the required tsort_each_child method, which fetches the array of child nodes and then iterates over that array using the user-supplied block.
require 'tsort' class Hash include TSort alias tsort_each_node each_key def tsort_each_child(node, &block) fetch(node).each(&block) end end {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort #=> [3, 2, 1, 4] {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components #=> [[4], [2, 3], [1]]
A More Realistic Example
A very simple âmakeâ like tool can be implemented as follows:
require 'tsort' class Make def initialize @dep = {} @dep.default = [] end def rule(outputs, inputs=[], &block) triple = [outputs, inputs, block] outputs.each {|f| @dep[f] = [triple]} @dep[triple] = inputs end def build(target) each_strongly_connected_component_from(target) {|ns| if ns.length != 1 fs = ns.delete_if {|n| Array === n} raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}") end n = ns.first if Array === n outputs, inputs, block = n inputs_time = inputs.map {|f| File.mtime f}.max begin outputs_time = outputs.map {|f| File.mtime f}.min rescue Errno::ENOENT outputs_time = nil end if outputs_time == nil || inputs_time != nil && outputs_time <= inputs_time sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i block.call end end } end def tsort_each_child(node, &block) @dep[node].each(&block) end include TSort end def command(arg) print arg, "\n" system arg end m = Make.new m.rule(%w[t1]) { command 'date > t1' } m.rule(%w[t2]) { command 'date > t2' } m.rule(%w[t3]) { command 'date > t3' } m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' } m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' } m.build('t5')
Bugs
-
âtsort.rbâ is wrong name because this library uses Tarjanâs algorithm for strongly connected components. Although âstrongly_connected_components.rbâ is correct but too long.
References
1. Tarjan, "Depth First Search and Linear Graph Algorithms",
SIAM Journal on Computing, Vol. 1, No. 2, pp. 146-160, June 1972.
TSort implements topological sorting using Tarjanâs algorithm for strongly connected components.
TSort is designed to be able to be used with any object which can be interpreted as a directed graph.
TSort requires two methods to interpret an object as a graph, tsort_each_node and tsort_each_child.
-
tsort_each_nodeis used to iterate for all nodes over a graph. -
tsort_each_childis used to iterate for child nodes of a given node.
The equality of nodes are defined by eql? and hash since TSort uses Hash internally.
A Simple Example
The following example demonstrates how to mix the TSort module into an existing class (in this case, Hash). Here, weâre treating each key in the hash as a node in the graph, and so we simply alias the required tsort_each_node method to Hashâs #each_key method. For each key in the hash, the associated value is an array of the nodeâs child nodes. This choice in turn leads to our implementation of the required tsort_each_child method, which fetches the array of child nodes and then iterates over that array using the user-supplied block.
require 'tsort' class Hash include TSort alias tsort_each_node each_key def tsort_each_child(node, &block) fetch(node).each(&block) end end {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort #=> [3, 2, 1, 4] {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components #=> [[4], [2, 3], [1]]
A More Realistic Example
A very simple âmakeâ like tool can be implemented as follows:
require 'tsort' class Make def initialize @dep = {} @dep.default = [] end def rule(outputs, inputs=[], &block) triple = [outputs, inputs, block] outputs.each {|f| @dep[f] = [triple]} @dep[triple] = inputs end def build(target) each_strongly_connected_component_from(target) {|ns| if ns.length != 1 fs = ns.delete_if {|n| Array === n} raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}") end n = ns.first if Array === n outputs, inputs, block = n inputs_time = inputs.map {|f| File.mtime f}.max begin outputs_time = outputs.map {|f| File.mtime f}.min rescue Errno::ENOENT outputs_time = nil end if outputs_time == nil || inputs_time != nil && outputs_time <= inputs_time sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i block.call end end } end def tsort_each_child(node, &block) @dep[node].each(&block) end include TSort end def command(arg) print arg, "\n" system arg end m = Make.new m.rule(%w[t1]) { command 'date > t1' } m.rule(%w[t2]) { command 'date > t2' } m.rule(%w[t3]) { command 'date > t3' } m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' } m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' } m.build('t5')
Bugs
-
âtsort.rbâ is wrong name because this library uses Tarjanâs algorithm for strongly connected components. Although âstrongly_connected_components.rbâ is correct but too long.
References
-
Tarjan, âDepth First Search and Linear Graph Algorithmsâ,
-
SIAM Journal on Computing, Vol. 1, No. 2, pp. 146-160, June 1972.
Constants
- VERSION
Public Class Methods
[T] (_EachNode[T] each_node, _EachChild[T] each_child) { (Array[T]) → void } → void
[T] (_EachNode[T] each_node, _EachChild[T] each_child) → Enumerator[Array[T], void]
[T] (^() { (T) → void } → void each_node, ^(T) { (T) → void } → void each_child) { (Array[T]) → void } → void
[T] (^() { (T) → void } → void each_node, ^(T) { (T) → void } → void each_child) → Enumerator[Array[T], void]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 145
def self.each_strongly_connected_component: [T] (_EachNode[T] each_node, _EachChild[T] each_child) { (Array[T]) -> void } -> void
| [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Enumerator[Array[T], void]
| [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) { (Array[T]) -> void } -> void
| [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Enumerator[Array[T], void]
The iterator version of the TSort.strongly_connected_components method.
The graph is represented by each_node and each_child. each_node should have call method which yields for each node in the graph. each_child should have call method which takes a node argument and yields for each child node.
g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} each_node = lambda {|&b| g.each_key(&b) } each_child = lambda {|n, &b| g[n].each(&b) } TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } #=> [4] # [2] # [3] # [1] g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} each_node = lambda {|&b| g.each_key(&b) } each_child = lambda {|n, &b| g[n].each(&b) } TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } #=> [4] # [2, 3] # [1]
[T] (T node, _EachChild[T] each_child, ?untyped id_map, ?untyped stack) { (Array[T]) → void } → void
[T] (T node, _EachChild[T] each_child, ?untyped id_map, ?untyped stack) → Enumerator[Array[T], void]
[T] (T node, ^(T) { (T) → void } → void each_child, ?untyped id_map, ?untyped stack) { (Array[T]) → void } → void
[T] (T node, ^(T) { (T) → void } → void each_child, ?untyped id_map, ?untyped stack) → Enumerator[Array[T], void]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 174
def self.each_strongly_connected_component_from: [T] (T node, _EachChild[T] each_child, ?untyped id_map, ?untyped stack) { (Array[T]) -> void } -> void
| [T] (T node, _EachChild[T] each_child, ?untyped id_map, ?untyped stack) -> Enumerator[Array[T], void]
| [T] (T node, ^(T) { (T) -> void } -> void each_child, ?untyped id_map, ?untyped stack) { (Array[T]) -> void } -> void
| [T] (T node, ^(T) { (T) -> void } -> void each_child, ?untyped id_map, ?untyped stack) -> Enumerator[Array[T], void]
Iterates over strongly connected components in a graph. The graph is represented by node and each_child.
node is the first node. each_child should have call method which takes a node argument and yields for each child node.
Return value is unspecified.
#TSort.each_strongly_connected_component_from is a class method and it doesnât need a class to represent a graph which includes TSort.
graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} each_child = lambda {|n, &b| graph[n].each(&b) } TSort.each_strongly_connected_component_from(1, each_child) {|scc| p scc } #=> [4] # [2, 3] # [1]
[T] (_EachNode[T] each_node, _EachChild[T] each_child) → Array[Array[T]]
[T] (^() { (T) → void } → void each_node, ^(T) { (T) → void } → void each_child) → Array[Array[T]]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 204
def self.strongly_connected_components: [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Array[Array[T]]
| [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Array[Array[T]]
Returns strongly connected components as an array of arrays of nodes. The array is sorted from children to parents. Each elements of the array represents a strongly connected component.
The graph is represented by each_node and each_child. each_node should have call method which yields for each node in the graph. each_child should have call method which takes a node argument and yields for each child node.
g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} each_node = lambda {|&b| g.each_key(&b) } each_child = lambda {|n, &b| g[n].each(&b) } p TSort.strongly_connected_components(each_node, each_child) #=> [[4], [2], [3], [1]] g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} each_node = lambda {|&b| g.each_key(&b) } each_child = lambda {|n, &b| g[n].each(&b) } p TSort.strongly_connected_components(each_node, each_child) #=> [[4], [2, 3], [1]]
[T] (_EachNode[T] each_node, _EachChild[T] each_child) → Array[T]
[T] (^() { (T) → void } → void each_node, ^(T) { (T) → void } → void each_child) → Array[T]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 232
def self.tsort: [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Array[T]
| [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Array[T]
Returns a topologically sorted array of nodes. The array is sorted from children to parents, i.e. the first element has no child and the last node has no parent.
The graph is represented by each_node and each_child. each_node should have call method which yields for each node in the graph. each_child should have call method which takes a node argument and yields for each child node.
If there is a cycle, TSort::Cyclic is raised.
g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} each_node = lambda {|&b| g.each_key(&b) } each_child = lambda {|n, &b| g[n].each(&b) } p TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1] g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} each_node = lambda {|&b| g.each_key(&b) } each_child = lambda {|n, &b| g[n].each(&b) } p TSort.tsort(each_node, each_child) # raises TSort::Cyclic
[T] (_EachNode[T] each_node, _EachChild[T] each_child) { (T) → void } → void
[T] (_EachNode[T] each_node, _EachChild[T] each_child) → Enumerator[T, void]
[T] (^() { (T) → void } → void each_node, ^(T) { (T) → void } → void each_child) { (T) → void } → void
[T] (^() { (T) → void } → void each_node, ^(T) { (T) → void } → void each_child) → Enumerator[T, void]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 255
def self.tsort_each: [T] (_EachNode[T] each_node, _EachChild[T] each_child) { (T) -> void } -> void
| [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Enumerator[T, void]
| [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) { (T) -> void } -> void
| [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Enumerator[T, void]
The iterator version of the TSort.tsort method.
The graph is represented by each_node and each_child. each_node should have call method which yields for each node in the graph. each_child should have call method which takes a node argument and yields for each child node.
g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} each_node = lambda {|&b| g.each_key(&b) } each_child = lambda {|n, &b| g[n].each(&b) } TSort.tsort_each(each_node, each_child) {|n| p n } #=> 4 # 2 # 3 # 1
Public Instance Methods
() { (Array[N]) → void } → void
() → Enumerator[Array[N], void]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 293
def each_strongly_connected_component: () { (Array[N]) -> void } -> void
| () -> Enumerator[Array[N], void]
The iterator version of the strongly_connected_components method. <em>obj</em>.each_strongly_connected_component is similar to <em>obj</em>.strongly_connected_components.each, but modification of obj during the iteration may lead to unexpected results.
each_strongly_connected_component returns nil.
class G include TSort def initialize(g) @g = g end def tsort_each_child(n, &b) @g[n].each(&b) end def tsort_each_node(&b) @g.each_key(&b) end end graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) graph.each_strongly_connected_component {|scc| p scc } #=> [4] # [2] # [3] # [1] graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) graph.each_strongly_connected_component {|scc| p scc } #=> [4] # [2, 3] # [1]
(N, ?untyped id_map, ?untyped stack) { (Array[N]) → void } → void
(N, ?untyped id_map, ?untyped stack) → Enumerator[Array[N], void]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 326
def each_strongly_connected_component_from: (N, ?untyped id_map, ?untyped stack) { (Array[N]) -> void } -> void
| (N, ?untyped id_map, ?untyped stack) -> Enumerator[Array[N], void]
Iterates over strongly connected component in the subgraph reachable from node.
Return value is unspecified.
each_strongly_connected_component_from doesnât call tsort_each_node.
class G include TSort def initialize(g) @g = g end def tsort_each_child(n, &b) @g[n].each(&b) end def tsort_each_node(&b) @g.each_key(&b) end end graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) graph.each_strongly_connected_component_from(2) {|scc| p scc } #=> [4] # [2] graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) graph.each_strongly_connected_component_from(2) {|scc| p scc } #=> [4] # [2, 3]
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 352
def strongly_connected_components: () -> Array[Array[N]]
Returns strongly connected components as an array of arrays of nodes. The array is sorted from children to parents. Each elements of the array represents a strongly connected component.
class G include TSort def initialize(g) @g = g end def tsort_each_child(n, &b) @g[n].each(&b) end def tsort_each_node(&b) @g.each_key(&b) end end graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) p graph.strongly_connected_components #=> [[4], [2], [3], [1]] graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 379
def tsort: () -> Array[N]
Returns a topologically sorted array of nodes. The array is sorted from children to parents, i.e. the first element has no child and the last node has no parent.
If there is a cycle, TSort::Cyclic is raised.
class G include TSort def initialize(g) @g = g end def tsort_each_child(n, &b) @g[n].each(&b) end def tsort_each_node(&b) @g.each_key(&b) end end graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) p graph.tsort #=> [4, 2, 3, 1] graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) p graph.tsort # raises TSort::Cyclic
() { (N) → void } → void
() → Enumerator[N, void]
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.1.1/stdlib/tsort/0/tsort.rbs, line 408
def tsort_each: () { (N) -> void } -> void
| () -> Enumerator[N, void]
The iterator version of the tsort method. <em>obj</em>.tsort_each is similar to <em>obj</em>.tsort.each, but modification of obj during the iteration may lead to unexpected results.
tsort_each returns nil. If there is a cycle, TSort::Cyclic is raised.
class G include TSort def initialize(g) @g = g end def tsort_each_child(n, &b) @g[n].each(&b) end def tsort_each_node(&b) @g.each_key(&b) end end graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) graph.tsort_each {|n| p n } #=> 4 # 2 # 3 # 1
# File vendor/bundle/ruby/4.0.0/gems/tsort-0.2.0/lib/tsort.rb, line 452 def tsort_each_child(node) # :yields: child raise NotImplementedError.new end
Should be implemented by a extended class.
tsort_each_child is used to iterate for child nodes of node.
# File vendor/bundle/ruby/4.0.0/gems/tsort-0.2.0/lib/tsort.rb, line 444 def tsort_each_node # :yields: node raise NotImplementedError.new end
Should be implemented by a extended class.
tsort_each_node is used to iterate for all nodes over a graph.