平等?,eql ?, ===和==有什么区别?

我想了解这四种方法之间的区别。 我知道默认==调用方法equal? 当两个操作数引用完全相同的对象时,它将返回true。

===默认情况下也调用equal?调用== equal? ...好吧,如果所有这三种方法都没有被覆盖,那么我猜=====equal? 做同样的事情?

现在来eql? 。 这是做什么的(默认情况下)? 它是否调用操作数的哈希/ ID?

为什么Ruby有这么多的平等标志? 他们是否应该在语义上有所不同?


我将在这里大量引用Object文档,因为我认为它有一些很好的解释。 我鼓励你阅读它,以及这些方法的文档,因为它们在其他类中被重写,比如String。

注意:如果你想在不同的物体上尝试这些,请使用下面的内容:

class Object
  def all_equals(o)
    ops = [:==, :===, :eql?, :equal?]
    Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })]
  end
end

"a".all_equals "a" # => {"=="=>true, "==="=>true, "eql?"=>true, "equal?"=>false}

== - 通用“平等”

在Object级别, ==只有在objother对象相同时才返回true。 通常,这个方法在后代类中被覆盖以提供类特定的含义。

这是最常见的比较,因此是您(作为课程作者)可以决定两个对象是否“相等”的最基本的地方。

=== - 病例平等

对于类Object而言,与调用#==类似,但通常被后代覆盖以在case语句中提供有意义的语义。

这非常有用。 有趣的===实现的例子:

  • 范围
  • 正则表达式
  • Proc(在Ruby 1.9中)
  • 所以你可以做这样的事情:

    case some_object
    when /a regex/
      # The regex matches
    when 2..4
      # some_object is in the range 2..4
    when lambda {|x| some_crazy_custom_predicate }
      # the lambda returned true
    end
    

    在这里看到我的答案,了解case + Regex如何使代码更加清晰。 当然,通过提供您自己的===实现,您可以获得自定义case语义。

    eql? - Hash平等

    eql? 方法返回true如果objother引用相同的散列键。 Hash使用它来测试成员是否相等。 对于Object类的Objecteql? ==同义。 子类通常通过别名eql?来继承这个传统eql? 到他们重写的==方法,但也有例外。 例如, Numeric类型通过==执行类型转换,但不跨eql? ,所以:

    1 == 1.0     #=> true
    1.eql? 1.0   #=> false
    

    所以你可以自由地覆盖这个为你自己的用途,或者你可以覆盖==和使用alias :eql? :== alias :eql? :==所以这两个方法的行为方式相同。

    equal? - 身份比较

    不像==equal? 方法不应被子类覆盖:它用于确定对象标识(即a.equal?(b) iff ab )是同一个对象)。

    这是有效的指针比较。


    我爱jtbandes的答案,但由于它很长,我会添加我自己的紧凑答案:

    =====eql? equal?
    是4个比较器,即。 在Ruby中比较两个对象的4种方法。
    因为在Ruby中,所有比较器(和大多数操作符)实际上都是方法调用,所以您可以自己更改,覆盖并定义这些比较方法的语义。 然而,当Ruby的内部语言结构使用哪个比较器时,了解这一点很重要:

    == (数值比较)
    Ruby使用:==无处不在比较2个对象的 ,例如。 哈希值:

    {a: 'z'}  ==  {a: 'Z'}    # => false
    {a: 1}    ==  {a: 1.0}    # => true
    

    === (案例比较)
    Ruby使用:===在case / when结构中。 以下代码片段在逻辑上是相同的:

    case foo
      when bar;  p 'do something'
    end
    
    if bar === foo
      p 'do something'
    end
    

    eql? (散列键比较)
    Ruby使用:eql? (结合方法散列)来比较哈希键。 在大多数类中:eql? 与以下内容相同:==。
    有关eql的知识? 只有当你想创建自己的特殊类时才是重要的:

    class Equ
      attr_accessor :val
      alias_method  :initialize, :val=
      def hash()           self.val % 2             end
      def eql?(other)      self.hash == other.hash  end
    end
    
    h = {Equ.new(3) => 3,  Equ.new(8) => 8,  Equ.new(15) => 15}    #3 entries, but 2 are :eql?
    h.size            # => 2
    h[Equ.new(27)]    # => 15
    

    注意:常用的Ruby类Set也依赖于哈希键比较。

    equal? (对象身份比较)
    Ruby使用:平等吗? 检查两个对象是否相同。 这个方法(类BasicObject)不应该被覆盖。

    obj = obj2 = 'a'
    obj.equal? obj2       # => true
    obj.equal? obj.dup    # => false
    

    平等操作符:==和!=

    ==运算符(也称为equals或double equal)将在两个对象相等时返回true,否则返回false。

    "koan" == "koan" # Output: => true
    

    !=运算符AKA不等式或bang-tilde与==相反。 如果两个对象不相等,它将返回true,如果两个对象相等,则返回false。

    "koan" != "discursive thought" # Output: => true
    

    请注意,具有相同元素的两个数组的顺序不同,相同字母的大写和小写版本不相等,依此类推。

    比较不同类型的数字(例如,整数和浮点数)时,如果它们的数值相同,==将返回true。

    2 == 2.0 # Output: => true
    

    等于?

    与测试两个操作数是否相等的==运算符不同,equals方法检查两个操作数是否引用同一个对象。 这是Ruby中最严格的平等形式。

    例如:a =“禅”b =“禅”

    a.object_id  # Output: => 20139460
    b.object_id  # Output :=> 19972120
    
    a.equal? b  # Output: => false
    

    在上面的例子中,我们有两个具有相同值的字符串。 但是,它们是两个不同的对象,具有不同的对象ID。 因此,平等? 方法将返回false。

    让我们再试一次,只有这个时候b会是一个参考。 请注意,两个变量的对象ID都相同,因为它们指向相同的对象。

    a = "zen"
    b = a
    
    a.object_id  # Output: => 18637360
    b.object_id  # Output: => 18637360
    
    a.equal? b  # Output: => true
    

    EQL?

    在Hash类中,eql? 它用于测试键是否相等。 需要一些背景来解释这一点。 在计算的一般环境中,散列函数采用任意大小的字符串(或文件)并生成称为散列码的固定大小的字符串或整数,通常称为散列。 一些常用的散列码类型是MD5,SHA-1和CRC。 它们用于加密算法,数据库索引,文件完整性检查等。一些编程语言(如Ruby)提供了一种称为散列表的集合类型。 散列表是类似字典的集合,它们成对存储数据,由唯一键和它们的相应值组成。 在引擎盖下,这些键被存储为散列码。 散列表通常被称为散列。 注意hashcan这个词是指一个哈希码还是一个哈希表。 在Ruby编程中,单词hash几乎总是指类似字典的集合。

    Ruby提供了一种称为哈希的内置方法来生成哈希码。 在下面的例子中,它需要一个字符串并返回一个哈希码。 注意具有相同值的字符串总是具有相同的哈希码,尽管它们是不同的对象(具有不同的对象ID)。

    "meditation".hash  # Output: => 1396080688894079547
    "meditation".hash  # Output: => 1396080688894079547
    "meditation".hash  # Output: => 1396080688894079547
    

    散列方法在包含在Object类中的Kernel模块中实现,该类是所有Ruby对象的默认根。 像Symbol和Integer这样的类使用默认实现,其他类如String和Hash提供它们自己的实现。

    Symbol.instance_method(:hash).owner  # Output: => Kernel
    Integer.instance_method(:hash).owner # Output: => Kernel
    
    String.instance_method(:hash).owner  # Output: => String
    Hash.instance_method(:hash).owner  # Output: => Hash
    

    在Ruby中,当我们将某些东西存储在散列(集合)中时,作为关键字(例如字符串或符号)提供的对象被转换并存储为散列码。 后来,当从哈希(集合)中检索元素时,我们提供了一个对象作为密钥,将其转换为散列码并与现有密钥进行比较。 如果匹配,则返回相应项目的值。 比较是使用eql? 方法在引擎盖下。

    "zen".eql? "zen"    # Output: => true
    # is the same as
    "zen".hash == "zen".hash # Output: => true
    

    在大多数情况下,eql? 方法的行为与==方法类似。 但是,有一些例外。 例如,eql? 在将整数与float进行比较时,不会执行隐式类型转换。

    2 == 2.0    # Output: => true
    2.eql? 2.0    # Output: => false
    2.hash == 2.0.hash  # Output: => false
    

    案例相等运算符:===

    许多Ruby的内置类(如String,Range和Regexp)提供了===运算符的自己实现,也称为case-equality,triple equals或threequals。 因为它在每个类中的实现方式不同,所以它的行为将有所不同,具体取决于所调用对象的类型。 通常,如果右侧的对象“属于”或“是左侧的对象的成员”,则返回true。 例如,它可以用来测试一个对象是否是一个类的实例(或它的一个子类)。

    String === "zen"  # Output: => true
    Range === (1..2)   # Output: => true
    Array === [1,2,3]   # Output: => true
    Integer === 2   # Output: => true
    

    其他可能最适合这项工作的方法也可以达到同样的结果。 通常尽可能明确地编写易于阅读的代码,而不会牺牲效率和简洁性。

    2.is_a? Integer   # Output: => true
    2.kind_of? Integer  # Output: => true
    2.instance_of? Integer # Output: => false
    

    请注意,最后一个示例返回false,因为整数(例如2)是Fixnum类的实例,它是Integer类的一个子类。 ===,is_a? 和instance_of? 如果对象是给定类或任何子类的实例,则方法返回true。 instance_of方法更严格,只有当对象是该类的实例时才返回true,而不是子类。

    is_a? 和kind_of? 方法在内核模块中实现,该模块由Object类混合。 两者都是相同方法的别名。 我们来验证一下:

    Kernel.instance_method(:kind_of?)== Kernel.instance_method(:is_a?)#输出:=> true

    ===的范围实现

    在范围对象上调用===运算符时,如果右侧的值落在左侧的范围内,则返回true。

    (1..4) === 3  # Output: => true
    (1..4) === 2.345 # Output: => true
    (1..4) === 6  # Output: => false
    
    ("a".."d") === "c" # Output: => true
    ("a".."d") === "e" # Output: => false
    

    请记住,===运算符调用左侧对象的===方法。 所以(1..4)=== 3等价于(1..4)。=== 3.换句话说,左边操作数的类将定义===方法的实现是称为,所以操作数位置不可互换。

    正则表达式实现===

    如果右侧的字符串与左侧的正则表达式匹配,则返回true。 / zen / ===“今日练习zazen”#输出:=> true#与“今日练习zazen”=〜/ zen /

    在case / when语句中隐式使用===运算符

    这个操作符也在case / when语句的引擎下使用。 这是它最常用的用法。

    minutes = 15
    
    case minutes
      when 10..20
        puts "match"
      else
        puts "no match"
    end
    
    # Output: match
    

    在上面的例子中,如果Ruby已经隐式使用了双等号运算符(==),那么范围10..20不会被认为等于一个整数,如15。它们匹配是因为三等号运算符(===)是隐含地用于所有情况/当语句。 上例中的代码等同于:

    if (10..20) === minutes
      puts "match"
    else
      puts "no match"
    end
    

    模式匹配运算符:=〜和!〜

    使用=〜(等于波浪号)和!〜(bang-tilde)运算符来针对正则表达式模式来匹配字符串和符号。

    String和Symbol类中的=〜方法的实现需要一个正则表达式(Regexp类的一个实例)作为参数。

    "practice zazen" =~ /zen/   # Output: => 11
    "practice zazen" =~ /discursive thought/ # Output: => nil
    
    :zazen =~ /zen/    # Output: => 2
    :zazen =~ /discursive thought/  # Output: => nil
    

    Regexp类中的实现需要一个字符串或符号作为参数。

    /zen/ =~ "practice zazen"  # Output: => 11
    /zen/ =~ "discursive thought" # Output: => nil
    

    在所有实现中,当字符串或符号与Regexp模式匹配时,它会返回一个整数,该整数是匹配的位置(索引)。 如果没有匹配,则返回nil。 请记住,在Ruby中,任何整数值都是“truthy”,nil是“falsy”,所以=〜运算符可用于if语句和三元运算符。

    puts "yes" if "zazen" =~ /zen/ # Output: => yes
    "zazen" =~ /zen/?"yes":"no" # Output: => yes
    

    模式匹配运算符对编写较短的if语句也很有用。 例:

    if meditation_type == "zazen" || meditation_type == "shikantaza" || meditation_type == "kinhin"
      true
    end
    Can be rewritten as:
    if meditation_type =~ /^(zazen|shikantaza|kinhin)$/
      true
    end
    

    !〜运算符与=〜相反,它在不匹配时返回true,如果匹配则返回false。

    更多信息可在此博客文章中找到。

    链接地址: http://www.djcxy.com/p/10021.html

    上一篇: What's the difference between equal?, eql?, ===, and ==?

    下一篇: What is the advantage of using Heredoc in PHP ?