作为一名新手,我通过大量关于如何重新定义平等的指南,但是他们都没有真正解释它如何运作,只是写什么。
所以,代码看起来像这样,
class Person
attr_reader :name
def initialize(name)
@name = name
end
def ==(whatever)
self.name == whatever.name
end
end
将考虑
name1 = Person.new("Jack")
和
name2 = Person.new("Jack")
相同,而旧的==
方法并不是因为它比较了其他东西。
但是,它实际上是如何工作的?什么是"无论什么" (很多人似乎在写#34;其他")它有什么作用?编辑:为了澄清,我理解这样做的意义,我只是不了解内部运作。一步一步发生什么? what.name做什么以及为什么做?如何在==方法中返回true / false值有助于重新定义它?这些是我在这里提出的问题。
答案 0 :(得分:1)
什么是"无论"
致电时:
name1 == name2
Ruby"翻译"这内部函数调用:
name1.==(name2)
此函数调用中的 ==
没有"特殊"意思是,它只是
函数名称(就像foo
或do_something
可能是函数名)。
如您所见,whatever
是传递给函数的参数,该函数是==
的右侧。
这称为"运算符重载&#34 ;;这意味着操作员可以根据变量做一些不同的事情;您可以对所有运营商执行此操作,例如>
(大于),=
(分配)等。
Here's a page page that deals with operator overloading in some more depth
为什么需要self.name而不仅仅是name或@name?
一切都有效;在这种情况下,name
,self.name
和@name
引用相同的变量。有些人更喜欢写self.name
,我就是其中之一,而且你更明确地指的是一个实例变量,而不是一个函数中的局部变量(更少&#34) ;魔&#34)。我也有很长的Python背景,self
是强制性的,所以我已经习惯了。
但是,只写name
似乎在Ruby中更常见。
而旧的==方法并不是因为它比较了其他东西。
来自the docs(强调我的):
在Object级别, ==仅在obj和其他时返回true 是同一个对象。通常,此方法被覆盖 后代类提供特定于类的含义。
因此,在您的情况下,name1
和name2
显然不是同一个对象,即使它具有相同的值。
Ruby无法知道你认为Person
的值是什么,所以你需要覆盖==
运算符,以便在你考虑两个对象是相同的。
答案 1 :(得分:1)
使用您自己的示例:
Person.new("Jack") == Person.new("Jack")
这里比较的是整个对象,它将具有不同的object_id
s。这将导致相等比较失败。
通过覆盖==
,您将使比较看到对您来说真正重要的内容,在本例中为name
属性。因此,您在第二个Person
对象中传递的内容是为了评估相等性,它实际上是在查看name
属性,而不是整个对象,就像覆盖==
之前所做的那样。要回答您的问题,您可以在比较中使用name
或@name
,这并不重要。 self
只是引用等式左边的对象,whatever
是右边的对象。
答案 2 :(得分:1)
OP的标题问"重新定义相等性如何在Ruby中实际工作?"。通用答案是:
Ruby有一些可以考虑的方法"平等":#==
,#equal?
,#eql?
,#===
,所有这些方法都有他们的具体使命与其他人略有不同。在幕后,#==
等运算符方法的工作原理如下:
42 == 42
被翻译成
42.==( 42 )
即,消息:==, 42
会被发送到号码42
:
42.send( :==, 42 )
当您在任何对象上重新定义#==
方法时,此重新定义的方法会处理该消息。
虽然其他答案提到Object#==
的作用,但您可以注意到对于可比对象(即您可以添加mixin Comparable
的那些对象),您应该重新定义方法#<=>
, mixin将自行处理#==
(以及#<
和#>
以及其他一些方法)。
此外,OP会提出轻微的语法问题:什么是whatever
,以及self.name
是否必要。这些与平等方法无关。
self.name
不是必需的,因为self
始终是隐式接收器。而不是self.name == whatever.name
,可以只编写name == whatever.name
。过度使用显式self
可以被认为是一种诡辩。 @name
不能使用,因为它意味着其他东西(引用实例变量)。
在方法定义
中def ==( whatever )
name == whatever.name
end
whatever
是一个参数。该方法使用单个参数whatever
定义。调用该方法时,它将需要一个参数,它将分配给局部变量whatever
。这只是方法定义语法,没有别的。
总而言之1>,OP的#==
版本要求将Person
类的对象按名称进行比较,如果名称相等,则认为它们相等,甚至虽然它们不是同一个对象。
答案 3 :(得分:1)
请在下面看到我的答案:
当您编写 name1 == name2 时,您实际上将name2变量发送到实例方法 的 ==(无论)强>
是传递给方法 == 的参数的名称,因为Ruby不要求您指定参数的类型,并且所有内容都是对象,因此您可以传递任何内容==方法。这就是为什么它写成“无论什么”,你可以命名你想要的任何东西。
编辑: what.name做什么
简而言之,它从任何对象中获取名为“name”的属性的值。
详细说明,Ruby会询问“无论”对象,是否可以响应“名称”消息。如果是,则返回该值,否则将引发异常。在您给出的比较方案中,显然name2实例响应“name”消息,因为您在Person类中定义了属性读取器 attr_reader:name 。
只要响应“name”消息,就可以将任何对象作为参数传递给==方法,因此“what”不必是Person实例。
答案 4 :(得分:1)
假设:
name1 = Person.new("Jack")
和
name2 = Person.new("Jack")
当你这样做时:
name1 == name2
实际发生的是您在==
对象上调用重新定义的name1
方法并将name2
对象作为参数传递。换句话说:
name1.==(name2)
在==
的定义中,该参数被称为whatever
。因此whatever
实际上是name2
。
一个简单的例子来说明上述观点:
def foo(bar)
puts bar
end
a = 'b'
foo(a) #=> "b"
因此,您将a
传递给foo
方法,但该参数名为bar
。因此,在foo
方法中使用bar
,而不是a
。
上述示例也是如此。在==
方法中,您使用的是whatever
,而不是name2
。