今天來分享 Ruby 裡的 == , === , eql? , equal? 。
物件相等與內容等同性
有時,特別是在物件導向編程中,對資料型別和繼承物件進行比對時,出現了相等性和辨別的問題。以下情況通常需要區別:
- 相同型別的兩個不同物件,例如兩隻手
- 兩個物件相等但不同,例如兩張 10 元鈔票
- 兩個物件相等但有不同的呈現,例如 $1 元紙鈔和 $1 元硬幣
-- 維基百科
== :最常使用,比較雙方的值如果相等,回傳 true。eql?:如果接收者和参数具有相同的類型和相等的值,回傳 true。我們試著來比較兩者的不同:
a = "abc"   # 印出 "abc"
b = a + ""  # 印出 "abc"
a == b      # 印出 true
a.eql?(b)   # 印出 true
c = 1.0     # 印出 1.0
d = 1       # 印出 1
c == d      # 印出 true
c.eql?(d)   # 印出 false
從以上例子看到 1 與 1.0 同樣皆為數值 1 ,但 eql? 除了比較直是否相等,還需比較類型,則 1 的類別為 Integer,1.0 的類別為 Float 。
=== :用於比較 case 语句的 when 是否相等記得之前我們在「流程控制與條件判斷」篇有分享 case..when ,這個方法的比較正是使用 === ,我們來舉個例子:
def calc_sum(param)
  if param.class == Array
    param.sum
  elsif param.class == Integer
    param
  else
    0
  end
end
puts calc_sum([1, 2, 3, 4, 5])  # 印出 15
puts calc_sum(5)                # 印出 5
puts calc_sum(nil)              # 印出 0
也許你會發現 case..when 其實是 if..else 加上 === 的語法糖,可以更口語化的使用你的程式語言是 Ruby 的賣點之一。
def calc_sum(param)
  if Array === param
    param.sum
  elsif Integer === param
    param
  else
    0
  end
end
def calc_sum(param)
  case param
  when Array
    param.sum
  when Integer
    param
  else
    0
  end
end
這裡需要注意幾件事:
1+1 時,其實是 1.+(1)。[1,2] === Array 與 Array === [1,2] 會得到不同結果,你可以將之想像成 Array.===([1,2,3]) 才是他真實的樣子。正規表示式 中有提到 "Regexp" 這個類別,也是用 === 來實作。我們一一來舉例:
[1,2,3] === Array      # 印出 false
Array === [1,2,3]      # 印出 true
[1,2,3].is_a?(Array)   # 印出 true
/cu/ === "cute"        # 印出 true
/\w/ === "cute"        # 印出 true
"cute" === /\w/        # 印出 false
相同object_id,是則回傳 true。a_string = "string"
another_string = "string"
a_string.equal?(another_string)   # 印出 false
a_string = :string
another_string = :string
a_string.equal?(another_string)   # 印出 true
a = "string".freeze
b = "string".frezze
a.equal?(b)                       # 印出 true
還記得 symbol 與 字串 的比較嗎? 同名的 symbol 只會有一個,它只佔了一塊記憶體的位置,因此會有相同的 object_id ,你可以試試 freeze 這個方法,你會發現某種程度它同時擁有兩者的優點,這或許是 Ruby 未來的版本宣稱要將字串預設為冷凍狀態的原因!
此文同步刊登於CJ-Han的網站