Ruby 是一种面向对象的语言。在面向对象的语言中,您向对象发送消息,然后这些对象以它们认为合适的方式响应这些消息。
这意味着消息的接收者,并且只有接收者(!)才能完全控制消息的含义。
但是,对于某些运算符,对对称性有一定的期望:例如,a == b
期望与 相同b == a
。但是在 OO 语言中这是不可能的!要么 要么a
必须b
是消息的接收者,因此在一种情况下a
决定是否相等,在另一种情况下a
决定。他们可能会做出不同的决定,然后对称性的期望就会被打破。b
b
因此,在某些类中,平等实际上是这样实现的:
如果我知道你是谁,我决定我们是否平等。但如果我不知道你是谁,我会让你决定,因为也许你知道我是谁!
这是一个例子:如果你编写自己的Numeric
类(比如一个Quaternion
类),那么系统内置Fixnum
类对 s 一无所知Quaternion
。所以,当你问Fixnum
0
它是否等于 时Quaternion
(0, 0, 0, 0)
,它会响应false
,即使那是错误的。
所以,相反,Fixnum
将首先检查:我知道如何将自己与 a 进行比较Quaternion
吗?不,我不知道,但也许 aQuaternion
知道如何将自己与 a 进行比较Fixnum
!毕竟在写类Quaternion
的时候这个类还不存在Fixnum
,所以Fixnum
类不能知道Quaternion
s。但是Quaternion
写这门课的时候,可能作者考虑的太周到了,让Quaternion
s和Fixnum
s比较成为可能。
这就是为什么Fixnum#==
扭转论点并再次尝试的原因。
,String
它是相同的,但有点复杂。在 Ruby 中,类不是类型,子类型和子类化是不同的。Ruby 本身根本没有类型的概念!对象的类型是它的协议,即它理解的消息以及它如何响应它们。但是这个概念并没有记录在 Ruby 中(例如,与 Objective-C 不同,它确实有一个明确的协议概念)。
但是,在某些情况下,您想打破 OO 封装,并了解特定类型,甚至更多:类型的特定表示。(注意:这违反了面向对象,但有时为了性能是必要的。)
如果 Ruby 需要一个对象属于特定类而不是仅仅响应特定协议,那么您将失去很多灵活性。例如,您必须使用 a String
,即使您更愿意使用 a Rope
。为了给你一些灵活性,Ruby 允许你传入一些不完全是 aString
但等价于 one 并且可以通过to_str
方法转换为 one 的东西。因此,与其他语言不同,其中A IS-A String
由A
的子类表示String
,在 Ruby 中,关系由A IS-A String
方法表示。A
to_str
这就是你在上面看到的。如果参数 toString#==
不是 a String
,那么String#==
不知道如何处理它。但是,如果它是“类似字符串的”,即它实现了to_str
,那么也许它确实知道如何将自己与 a 进行比较String
?
请注意,平等很难做到正确。人们甚至无法就纯函数式语言的含义达成一致,这很简单!在 Ruby 中,还有两个额外的复杂性:可变状态和 OO。可变状态意味着片刻前相等的两个对象可能在片刻之后不再相等。或者他们应该是?还是不应该?OO意味着相等不能是对称的。
这就是为什么==
各种核心和标准库类的实现不断改进的原因。这也是为什么你会时不时地看到奇怪的行为的原因。有时它可能只是试图使平等正确的产物,有时它可能只是一个错误。
顺便说一句:对于算术运算符,子类实际上有一个更正式的使用该方法Numeric
的双重调度协议。coerce
如果一个Numeric
对象不知道如何处理另一个Numeric
对象,它会向另一个对象询问这coerce
两个对象到一个知道这一点的类型。例如,如果您尝试将 a 添加Quaternion
到 a Fixnum
,Fixnum
则将不知道该怎么做:
2 + Quaternion.new(1, 0, 0, 0)
然后将调用的+
方法:Fixnum
a, b = other.coerce(self)
IOW:它将调用Quaternion#coerce
相当于
Quaternion.new(1, 0, 0, 0).coerce(2)
将Quaternion
响应Array
_[Quaternion.new(2, 0, 0, 0), Quaternion.new(1, 0, 0, 0)]
然后,Fixnum#+
只需调用
a + b
现在可以使用,因为a
它也是 aQuaternion
并且知道如何添加两个Quaternion
s。
一个非常常见的实现coerce
是简单地交换参数,即
def coerce(other)
return other, self
end
这相当于您看到的行为Fixnum#==
。
再说一遍:这种调度很难做到正确,并且正在对coerce
协议进行改进。