如何重新定义平等在Ruby中实际工作?

时间:2014-12-17 13:13:12

标签: ruby equality

作为一名新手,我通过大量关于如何重新定义平等的指南,但是他们都没有真正解释它如何运作,只是写什么。

所以,代码看起来像这样,

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值有助于重新定义它?这些是我在这里提出的问题。

5 个答案:

答案 0 :(得分:1)

  

什么是"无论"

致电时:

name1 == name2

Ruby"翻译"这内部函数调用:

name1.==(name2)
此函数调用中的

==没有"特殊"意思是,它只是 函数名称(就像foodo_something可能是函数名)。

如您所见,whatever是传递给函数的参数,该函数是==的右侧。

这称为"运算符重载&#34 ;;这意味着操作员可以根据变量做一些不同的事情;您可以对所有运营商执行此操作,例如>(大于),=(分配)等。

Here's a page page that deals with operator overloading in some more depth

  

为什么需要self.name而不仅仅是name或@name?

一切都有效;在这种情况下,nameself.name@name引用相同的变量。有些人更喜欢写self.name,我就是其中之一,而且你更明确地指的是一个实例变量,而不是一个函数中的局部变量(更少&#34) ;魔&#34)。我也有很长的Python背景,self是强制性的,所以我已经习惯了。
但是,只写name似乎在Ruby中更常见。

  

而旧的==方法并不是因为它比较了其他东西。

来自the docs(强调我的):

  

在Object级别, ==仅在obj和其他时返回true   是同一个对象。通常,此方法被覆盖   后代类提供特定于类的含义。

因此,在您的情况下,name1name2显然不是同一个对象,即使它具有相同的
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。这只是方法定义语法,没有别的。

总而言之,OP的#==版本要求将Person类的对象按名称进行比较,如果名称相等,则认为它们相等,甚至虽然它们不是同一个对象。

答案 3 :(得分:1)

请在下面看到我的答案:

  1. 但是,它实际上是如何运作的?
  2. 当您编写 name1 == name2 时,您实际上将name2变量发送到实例方法 的 ==(无论)

    1. 什么是“什么”(许多人似乎在写“其他”)以及它做了什么?
    2. 是传递给方法 == 的参数的名称,因为Ruby不要求您指定参数的类型,并且所有内容都是对象,因此您可以传递任何内容==方法。这就是为什么它写成“无论什么”,你可以命名你想要的任何东西。

      1. 为什么需要self.name而不仅仅是name或@name?
      2. <=>在==方法中,self是当前实例,所以当你写name1 == name2时, self name1 。使用self.name与使用 @name 相同。如果您只是编写之前未定义的名称,则其值将为 nil 。这将使比较变为 nil == whatever.name 。这可能不是你想要的。

        编辑: 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