Ruby code整理系列 ||= 的使用
本篇簡單說明利用||=符號將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,中文尚未看到統一的稱呼,還請強者補充。
這樣的寫法邏輯說明:回傳||左邊的值,但假如左邊回傳nil
或false
,改成把右邊的值指定給左邊,並回傳。第二種方法邏輯相同,但在這裡不比第一種直覺,僅供參考。
當然,既然是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儲存起來。說實在,會遇到這樣的例外狀況應該非常少,不過假如真的很不幸在這種地方卡關,千萬要記得有這個情況喔!