前幾天我們把焦點集中在True or False(判斷真假),今天來研究幾個比較是否相等的方法!在程式世界裡,真假與相等都有更多元、更超乎想像的概念。如果是程式新手,可能會對這幾個看起來特別像的運算子:==, ===, eql?, equal?充滿黑人問號...???
趕快來看看今天的題目!
Day23 解釋Ruby裡的 ==, ===, eql?, equal。 Explain each of the following operators  ==, ===, eql?, equal?
有些方法比其他的方法更相等(Some are more equal than others)。這是怎麼回事?讓我們繼續看下去~
==== 檢查兩個運算子的值是否相等 (check if the value of two operands are equal)
eql?如果接收器和參數的值和類型都相等,則為true. ( checks if the value and type of two operands are the same ),
來比較一下==和.eql?的用法:
100 == 100.0 #=> true
100.eql?(100.0) #=> false
從以上例子看到100與100.0同樣皆為數值1,但1的類型為Fixnum,1.0的類型為Float浮點數(包含小數點)。
====== 測試case語法中、when子句內的相等性 (test equality within the when clause of a case statement)
舉個生活化的例子來描述case語法:
下週我要出遠門旅行,因此我規劃去超市裡購物,在旅行大背包裡放一些食物補給品。我想要以[第7天]學到的符號Symbol將food歸類:
 def type_of(food)
  case food
  when 'Apple'
    :fruit
  when 'Banana'
    :fruit
  when 'Chocolate'
    :sweet
  when 'Noodles'
    :meal
  when 'Chips'
    :junkfood
  else
    :unknown
  end
end
p type_of 'Chips' # => :junkfood
hmmm....被發現買洋芋片是垃圾食物了了!
===代表的是 case equality
以上的case...when,其實是從if...else加上===的語法改寫而來。
def type_of(food)
  if 'apple' === food
    :fruit
  elsif 'Chocolate' === food
    :sweet
  elsif 'Noodles' === food
    :meal
  elsif 'Chips' === food
    :junkfood
  else
    :unknown
  end
p type_of 'Nuts' # => :unknown
旅行時買一些腰果核桃補充能量,比洋芋片建康一些。XD
補充一下:Ruby的when後面可以放多個參數,讓程式更簡潔,因此我來放更多的食物Orange和Bread進去旅行大背包:
 def type_of(food)
  case food
  when 'Apple', 'Banana', 'Orange'
    :fruit
  when 'Chocolate'
    :sweet
  when 'Noodles', 'Bread'
    :meal
  when 'Chips'
    :junkfood
  else
    :unknown
  end
end
p type_of 'Orange' # => :fruit
從以上大背包放食物的例子我們可以發現,===比的是case equality in case statement(更近一步來說,比的是上層的類別物件class object),所以以下四條判斷式,會顯示為全部為真:
p Class  === Class
p Object === Object
p Class  === Object
p Object === Class
# all true. 萬物皆為物件!
比較tricky的部分是Fixnum,我發現到了如果將Fixnum擺在===(threequality)的右邊,結果為假:
p 1 === 1 # => true
p Fixnum === 1 # => true
p 1 === Fixnum # => false
p Fixnum === Fixnum # => false
#warning: constant ::Fixnum is deprecated
Ruby Gotchas這份slide説明:
A better name (IMHO, 以我的觀點來說) might be
.describes?, or overload.includes?
這或許就是為什麼我們不能把拿來比較的參數放在左邊,而是右邊:)
使用===的好處就是可以用正規表示式提取、比較更多符合我們需求的條件。從以上的結論我們發現要將正規表示式放左邊。參考Ruby Doc 關於Regexp方法下的Public Instance Methods這頁說明,我們可以分別用 ===和 == 了解各項舉例是否為真:
p /banana/ === 'banana' # => true
p /banana/ === /banana/ # => false
p /banana/ == 'banana' # => false
p /banana/ == /banana/ # => true
為何在上述banana的例子裡,/banana/ === 'banana'為真,而/banana/ == 'banana' 為假呢?
Regexp#===是用來比對(match)字串是否包含/正規表示式/裡的字符號 (tests whether or not the argument matches the regular expression.)。而在此例子裡,'banana'字串的確包含banana這些字符號。
但/banana/和'banana'本身並不是同一個值,所以==結果為假。
equal?equal? : 如果接收器和參數的物件id(記憶體位置)相同,則為true (compares if both operands refer to the same object i.e. have the same object id)
最後我們來用Ruby is Awesome作為總結吧!
ruby = "awesome"
rails = "awesome"
p ruby == rails   # => true #ruby和rails都很awesome!
p ruby.eql? rails # => true
# ruby和rails不但都很awesome, 而且兩者的類型都是字串(string)!
p ruby.equal? rails # => false
#ruby和rails分別存在不同的記憶體位置,它們不是同一個物件
p ruby.object_id #70263932897220
p rails.object_id #70263932897160
超級比一比:
| == (等於) | === | eql? | equal? | 
|---|---|---|---|
| 檢查兩個運算子的值是否相等 | 測試case語法中的 when子句相等性(object class) | 如果接收器和 參數的值和類型都相等,則為true | 如果接收器和參數的 object id相同,則為true | 
Ref: