在 Ruby 中覆盖 Spaceship 运算符时实际发生了什么?

What actually happens when Spaceship operator is overwritten in Ruby?

我是 Ruby 的新手。有人可以解释 <=> 方法在下面的程序中是如何工作的吗?下面的 'other' 参数是如何使用的?

class Age
  attr_reader :value

  def initialize(value)
  @value = value
  end

  def <=> (other) # What is actually happening here...
  self.value <=> other.value # ..and here?
  end

end

a = Age.new(7)
b = Age.new(3)
c = Age.new(9)

d = [a, b, c]
puts d.sort

让我们从d.sort开始。 d 是一个数组,所以你应该阅读 Array#sort。文档说排序 "will be done using the <=> operator"。这意味着在对数组进行排序时,它将重复计算 x <=> y,其中 xyd 的不同元素,以确定哪些元素是 bigger/smaller。

Ruby中的运算符有点棘手,因为它们实际上是变相的方法调用。 x <=> y只是x.<=>(y)的另一种写法。也就是说,x 有一个名为 <=> 的方法,它作为参数传递给 y。因为d的元素是Ageclass的实例,所以Age需要定义实例方法<=>。所以当你看到 def <=> (other) 时,这与普通的方法定义(def foo(other))并没有什么不同,只是方法名称不同而已。

既然我们正在为 Age 个实例定义 <=>,该方法实际上应该做什么?那么每个 Age 对象存储一个 @value,所以直觉上 Age#<=> 应该比较 @value。这就是 self.value <=> other.value 所做的。这是可行的,因为 value returns 和 Fixnum,幸运的是 Fixnum#<=> 内置于 Ruby 中以进行正确的比较。

关于幕后发生的事情的示例,我们可以尝试对包含我们不知道如何比较的值的数组进行排序:

[Age.new(3), 4].sort
# NoMethodError: undefined method `value' for 4:Fixnum
[4, Age.new(3)].sort
# ArgumentError: comparison of Fixnum with Age failed

这显示了对不同类型进行排序如何导致调用不同的 <=> 方法。