Ruby code整理系列 ||= 的使用


ruby_or_equal

本篇簡單說明利用||=符號將if邏輯簡化。

1. 情境:檢查tag是否存在

假如今天我們在發佈post時,需要在model中檢查作者是否有預設的tag,沒有的話就標記為"untagged":

before_save :assign_tag

def assign_tag
    self.tag = "untagged" unless defined?(self.tag)
end

上方的邏輯簡單解釋,就是先判斷tag有沒有在post的創建過程中宣告過了,如果沒有,就將tag的內容指定為"untagged";如果有宣告過的話,這邊就當作什麼都沒發生,使用原本作者指定的值。

2. 簡化

這樣的邏輯只剩一行了,還可以怎麼簡化呢?重點在於這樣的句法唸起來有點繞口,而且self.tag出現了兩次。在邏輯的處理上其實可以更進一步簡化:

def assign_tag
    self.tag ||= "untagged"
    # 或者...
    self.tag || self.tag = "untagged"
end

再次印證了『沒有最簡單,只有更簡單』的概念,第一個方法中間的||= 符號目前我尚未找到正式名稱,許多英文開發者稱為 double pipeline equal 或 or equal,中文尚未看到統一的稱呼,還請強者補充。

這樣的寫法邏輯說明:回傳||左邊的值,但假如左邊回傳nilfalse,改成把右邊的值指定給左邊,並回傳。第二種方法邏輯相同,但在這裡不比第一種直覺,僅供參考。

當然,既然是Rails,也可以利用Rails的ActiveRecord來解,可以在migration裡加上:default => "untagged"讓tag欄位在儲存時若沒有指定也會有default值。

3. 其他類似的or(||)用法

另一個相似的寫法如下:

result = a || "a exists" 

這樣的寫法等於:

if defined?(a)
    result = a
else
    result = "a exists"
end

不過要注意的是,由於要判斷a是否存在,所以a一定是要宣告過的數值(例如 a = nil),否則會出現undefined local variable錯誤,不像 ||= 的用法不需要事先宣告。

4. Hash的例外情況

大家應該都知道以下用法:

a = Hash.new(10)

在括弧內的值,代表任何該Hash內尚未宣告的預設值,但並不會儲存起來,使用情況如下:

#以下指令在rails console照順序執行

a
# => {}

a[:number]
# => 10

a
# => {}

a[:number] = 25
# => 25

a
# => {:number => 25}

如果隨便使用hash當中的一個key,則出現的value就是預設值,但並不會儲存。除非我們使用宣告的方式進行指定,該key與value才會儲存起來。

不過,套用剛剛的or(||)用法,會看到以下的例外狀況:

a = Hash.new(10)
# => {}

a[:number] = a[:number] || 25
# => 10

a
# => {:number => 25}

在第二個步驟,有一個指定的預設value是10,於是hash就把這個value儲存起來。說實在,會遇到這樣的例外狀況應該非常少,不過假如真的很不幸在這種地方卡關,千萬要記得有這個情況喔!

延伸閱讀

Ruby Inside