Search code examples
rubyclassmodulesubclassposet

Creating Class relation


Given an array of modules, what is the best way to return an array that describes the normalized (minimal) ordering relations between the modules? Each element of the array should be an array of pairs of modules that have child-parent relation. The child-parent order within each pair matters, but the order among the pairs does not matter. Normalized ordering means that whatever can be derived from transitivity should be excluded from the array.

For example, given [Object, Comparable, Float, Fixnum, Integer], the answer would be:

[
  [Float, Object],
  [Float, Comparable],
  [Fixnum, Integer],
  [Integer, Object],
  [Integer, Comparable],
]

The five pairs in the array corresponds to the five edges in this Hasse diagram: Hasse diagram

Hint: Module#<=>other returns -1, 0, 1 if there is an order relation, and nil if there is no order relation.


Solution

  • def ordering(mods)
      a = mods.permutation(2)
              .select { |m1,m2| (m1<=>m2) == -1 }
      a.reject { |m1,m2|
          mods.any? { |m| a.include?([m1,m]) && a.include?([m,m2]) } }
    end
    
    ordering([Object, Comparable, Float, Fixnum, Integer])
      #=> [[Float, Object],
      #    [Float, Comparable],
      #    [Fixnum, Integer],
      #    [Integer, Object],
      #    [Integer, Comparable]] 
    

    mods = [Object, Comparable, Float, Fixnum, Integer, String, Array,
            Hash, Enumerable, Enumerator, Module, Method, IO, File]
    ordering(mods)
      #=> [[Float, Object], [Float, Comparable],
      #    [Fixnum, Integer],
      #    [Integer, Object], [Integer, Comparable],
      #    [String, Object], [String, Comparable],
      #    [Array, Object], [Array, Enumerable],
      #    [Hash, Object], [Hash, Enumerable], [Hash, Object],
      #      [Hash, Enumerable],
      #    [Enumerator, Object], [Enumerator, Enumerable],
      #    [Module, Object],
      #    [Method, Object],
      #    [IO, Object], [IO, Enumerable],
      #    [File, IO]]