テキスト差分プログラムRuby版

昨日書いたテキスト差分プログラムRuby版がとりあえず動きました。

ソースは以下のような感じです。

class Diff
  def initialize(a, b)
    @path = Hash.new
    if a.length < b.length then
      @a = a
      @b = b
    else
      @a = b
      @b = a
    end
    
    # push dummy.
    @a.unshift(nil)
    @b.unshift(nil)
    @fp = Hash.new(-1)
    @lcs = onp()
  end

  def max(*arguments)
    max = nil
    arguments.each{ |arg|
      max = arg if max.nil? || max < arg
    }

    return max
  end

  def getm
    return @a.length - 1
  end

  def getn
    return @b.length - 1
  end
    
  def onp
    m = getm()
    n = getn()
    delta = n - m
    p = 0
    loop do
      (-p).upto(delta - 1){ |k|
        @fp[k] = snake(k)
      }

      (delta + p).downto(delta + 1){ |k|
        @fp[k] = snake(k)
      }

      k = delta
      y = snake(k)
      @fp[k] = y

      p+=1
      break if @fp[delta] == n
    end
    
    return @path[delta]
  end

  def snake(k)
    i = @fp[k - 1]  + 1
    j = @fp[k + 1]

    v = 0
    if i > j then
      @path[k] = @path[k - 1].dup if @path[k - 1]
      v = 1
    else
      @path[k] = @path[k + 1].dup if @path[k + 1]
      v = -1
    end
    @path[k] = [] if @path[k].nil?
    @path[k].push(v)
    
    y = max(i, j)
    x = y - k
    while (x < getm()  &&  y < getn()) && compare(@a[x+1], @b[y+1])
      x+=1
      y+=1
      @path[k].push(0)
    end
    
    return y
  end

  def compare(a, b)
    return a == b
  end

  def asText
    a = @a.dup
    a.shift
    b = @b.dup
    b.shift

    l = @lcs.dup
    l.shift
    diff = l.map{ |n|
      if n > 0 then
        "+\t" + " \t" + b.shift().to_s
      elsif n < 0 then
        "-\t" + a.shift().to_s
      else
        "=\t" + a.shift().to_s + "\t" + b.shift().to_s
      end
    }
    content = diff.join("\n")
    return content
  end
end

a = [1, 2, 4, 8, 16]
b = [1, 2, 3, 4, 5, 6, 7, 8]
onp = Diff.new(a, b)
puts onp.asText

昨日も書きましたが、動くまでにかなり苦労しました。

もとの理論をよく理解していないので、ひたすらJavaScriptのロジックを追うしかありません。最後はFireBugJavaScriptを、NetBeansRubyをステップ実行しながら、各所で変数の内容を比べるという荒業でなんとか動かしました。

また、配列で負のインデックスが発生する部分は、オフセットを付けたり専用の配列クラスを作成したりすればいいですが、面倒なのでハッシュで代用してしまいました。