拥有太空飞船运营商的自有班级不适用于LCS Diff

时间:2014-03-06 06:28:02

标签: ruby diff comparable

我正在尝试实现一个自定义类,包括可比较的mixin。后来这个课用来做差异。该课程看起来像

class Element
  include Comparable
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def <=>(other)
    @name <=> other.name
  end
end

现在一些测试值包含两个添加的条目与a相比。

a = Array.new
b = Array.new

a.push Element.new('y1')
a.push Element.new('y2')
a.push Element.new('y4')
a.push Element.new('x1')
a.push Element.new('x2')
a.push Element.new('x4')

b.push Element.new('y1')
b.push Element.new('y2')
b.push Element.new('y3')
b.push Element.new('y4')
b.push Element.new('x1')
b.push Element.new('x2')
b.push Element.new('x3')
b.push Element.new('x4')

现在在两个数组之间运行差异

puts Diff::LCS.diff(a, b).inspect

我希望现在有两个添加的对象,但它会找到8个更改...有什么想法吗?

[[["-", 2, #<Element:0x007fa9fb567898 @name="y4">],
  ["-", 3, #<Element:0x007fa9fbc69830 @name="x1">],
  ["-", 4, #<Element:0x007fa9fbca3378 @name="x2">],
  ["+", 2, #<Element:0x007fa9fb5e5e78 @name="y3">],
  ["+", 3, #<Element:0x007fa9fb606920 @name="y4">],
  ["+", 4, #<Element:0x007fa9fb625848 @name="x1">],
  ["+", 5, #<Element:0x007fa9fb647da8 @name="x2">],
  ["+", 6, #<Element:0x007fa9fbde8670 @name="x3">]]]

如果我们用

运行测试
a = Array.new
b = Array.new

a.push 'y1'
a.push 'y2'
a.push 'y4'
a.push 'x1'
a.push 'x2'
a.push 'x4'

b.push 'y1'
b.push 'y2'
b.push 'y3'
b.push 'y4'
b.push 'x1'
b.push 'x2'
b.push 'x3'
b.push 'x4'

一切都按预期工作:

[[["+", 2, "y3"]],
 [["+", 6, "x3"]]]

1 个答案:

答案 0 :(得分:2)

Diff::LCS的来源检查,序列中的元素被用作散列键。

您编写的Element类混合了Comparable模块,它在那里得到了==方法。但是,它没有eql?hash方法覆盖,Hash用来确定密钥比较。

Element课程中,我们有

irb(main):013:0> a = Element.new("a")
=> #<Element:0x002ae4ce402da0 @name="a">
irb(main):014:0> b = Element.new("a")
=> #<Element:0x002ae4ce409ec0 @name="a">
irb(main):015:0> a == b
=> true
irb(main):016:0> a.eql? b
=> false
irb(main):017:0> a.hash == b.hash
=> false
irb(main):018:0> h = {}
=> {}
irb(main):019:0> h[a] = 1
=> 1
irb(main):020:0> h[b]
=> nil

这会影响LCS计算。

我认为修复是为eql?类添加hashElement方法。

class Element
  include Comparable
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def <=>(other)
    @name <=> other.name
  end

  def eql? other
    other.class == Element and self.name == other.name
  end

  def hash
    @name.hash ^ Element.hash
  end
end