[前][次][番号順一覧][スレッド一覧]

ruby-changes:69511

From: Frederik <ko1@a...>
Date: Fri, 29 Oct 2021 18:19:10 +0900 (JST)
Subject: [ruby-changes:69511] f45af5f0a4 (master): Support gemification of tsort

https://git.ruby-lang.org/ruby.git/commit/?id=f45af5f0a4

From f45af5f0a427a85fd157bbb1461a7dc33d30eb1b Mon Sep 17 00:00:00 2001
From: Frederik Dudzik <frederik.dudzik@s...>
Date: Tue, 19 Oct 2021 12:35:35 -0700
Subject: Support gemification of tsort

Co-authored-by: Frederik Dudzik <frederik.dudzik@s...>
Co-authored-by: Jacques Chester <jacques.chester@s...>
---
 lib/bundler/spec_set.rb                            |   2 +-
 .../molinillo/lib/molinillo/dependency_graph.rb    |   4 +-
 lib/bundler/vendor/tsort/lib/tsort.rb              | 453 +++++++++++++++++++++
 lib/bundler/vendored_tsort.rb                      |   4 +
 spec/bundler/runtime/setup_spec.rb                 |   2 +-
 5 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 lib/bundler/vendor/tsort/lib/tsort.rb
 create mode 100644 lib/bundler/vendored_tsort.rb

diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index 26d41cb9b8d..a19d18388ab 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -1,6 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/lib/bundler/spec_set.rb#L1
 # frozen_string_literal: true
 
-require "tsort"
+require_relative "vendored_tsort"
 
 module Bundler
   class SpecSet
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
index 936399ed2f5..10a25d2f08a 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
@@ -1,6 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb#L1
 # frozen_string_literal: true
 
-require 'tsort'
+require_relative '../../../../vendored_tsort'
 
 require_relative 'dependency_graph/log'
 require_relative 'dependency_graph/vertex'
@@ -17,7 +17,7 @@ module Bundler::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb#L17
       vertices.values.each { |v| yield v }
     end
 
-    include TSort
+    include Bundler::TSort
 
     # @!visibility private
     alias tsort_each_node each
diff --git a/lib/bundler/vendor/tsort/lib/tsort.rb b/lib/bundler/vendor/tsort/lib/tsort.rb
new file mode 100644
index 00000000000..8454583295e
--- /dev/null
+++ b/lib/bundler/vendor/tsort/lib/tsort.rb
@@ -0,0 +1,453 @@ https://github.com/ruby/ruby/blob/trunk/lib/bundler/vendor/tsort/lib/tsort.rb#L1
+# frozen_string_literal: true
+
+#--
+# tsort.rb - provides a module for topological sorting and strongly connected components.
+#++
+#
+
+#
+# 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_node is used to iterate for all nodes over a graph.
+# * tsort_each_child is 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
+#
+# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
+# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
+#
+module Bundler
+  module TSort
+    class Cyclic < StandardError
+    end
+
+    # 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
+    #
+    def tsort
+      each_node = method(:tsort_each_node)
+      each_child = method(:tsort_each_child)
+      TSort.tsort(each_node, each_child)
+    end
+
+    # 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
+    #
+    def TSort.tsort(each_node, each_child)
+      TSort.tsort_each(each_node, each_child).to_a
+    end
+
+    # The iterator version of the #tsort method.
+    # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, 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
+    #
+    def tsort_each(&block) # :yields: node
+      each_node = method(:tsort_each_node)
+      each_child = method(:tsort_each_child)
+      TSort.tsort_each(each_node, each_child, &block)
+    end
+
+    # 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
+    #
+    def TSort.tsort_each(each_node, each_child) # :yields: node
+      return to_enum(__method__, each_node, each_child) unless block_given?
+
+      TSort.each_strongly_connected_component(each_node, each_child) {|component|
+        if compo (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]