RailsWizard中的排序算法存在问题

问题描述

| RailsWizard是一种生成Rails模板的工具,它可以通过指定要在给定配方之前(
run_before
选项)和之后(ѭ1which)运行哪些配方来设置配方顺序,如https://github.com/intridea/rails_wizard/wiki/中所述配方配置 这是在“ 2”类中实现的:
module RailsWizard
  class Recipe
    extend Comparable

    def self.<=>(another)
      return -1 if another.run_after.include?(self.key) || self.run_before.include?(another.key)
      return 1 if another.run_before.include?(self.key) || self.run_after.include?(another.key)
      self.key <=> another.key # simple sort by name
    end
  end
end
但是,此算法通常会提供错误的顺序,请参见相应的问题以及此问题的演示脚本:https://gist.github.com/987025 您将如何解决此算法?     

解决方法

        该算法的问题在于比较运算符未定义严格弱排序。 具体来说,它缺乏传递性(如果x
before?
比较运算符的代码。
require \'yaml\'
hash = YAML.load \'
a:
  after: 
  before: 
l:
  after: 
  before: 
n:
  after: 
  before: l
b:
  after: 
  before: 
m:
  after: 
  before: 
c:
  after: 
  before: 
z:
  after: 
  before: 
\'
hash.each do |k,v|
  v.each do |vk,vv|
    v[vk] = (vv||\'\').split
  end
end

class Hash
  def without *ks
    delete_if { |k,v| ks.include? k }
  end
  def delete! *ks
    #print \"\\n\\nwithout: #{ks.join(\',\')}\"
    replace without *ks
  end
end

def print_keys hash
  puts hash.map(&:first).join(\'  \')
end

def sort hash
  array = hash.to_a
  selection_sort array
  print_keys array
  Hash[array]
end

def selection_sort array
  (0...array.length).each do |i|
    min = i
    ((i+1)...array.length).each do |j|
      min = j if before?(array[j],array[min])
    end
    temp = array[min]
    array[min] = array[i]
    array[i] = temp
  end
end

def before? a,b
  b.last[\'after\'].include?(a.first) || a.last[\'before\'].include?(b.first)
end

def test hash
  puts nil,nil
  print_keys hash
  puts \'-\'*hash.count*3
  hash.count.times { hash = sort hash }
end

test hash
这给出了:
$ ruby sort_test.rb 


a  l  n  b  m  c  z
---------------------
a  n  l  b  m  c  z
a  n  l  b  m  c  z
a  n  l  b  m  c  z
a  n  l  b  m  c  z
a  n  l  b  m  c  z
a  n  l  b  m  c  z
a  n  l  b  m  c  z