Ruby'陷阱'之: '||=' 的真正展開式

[i]前一段時間,我在這裏[url]http://rubynroll.iteye.com/blog/192547[/url]展示了一個空格帶來的'陷阱', 今天又見到另一個'陷阱'(http://dablog.rubypal.com/2008/3/25/a-short-circuit-edge-case by David).[/i]

之所以爲陷阱加引號, 是因爲大部分情況下我們都沒有機會掉進去 :)

[b]大多數Ruby教科書在解釋 "a ||= b" 這個複合操作時,都說她等效於: "a = a || b", 實際上真的如此麼?[/b]

讓我們在irb裏面來看看:

h = Hash.new(1)  # 生成一個新的Hash,缺省值爲1
=> {}
h[:x]
=> 1 # h[:x]未定義,所以返回1
h[:x] ||= 2
=> 1
h
=> {} # 啊? '陷阱'?
h[:x] = h[:x] || 2
=> 1
h
=> {:x=>1} # 啊!


David提到,[b]Matz在RubyConf 2007上的解釋是"a ||= b"真實的等價應該是: "a or a = b", 但是David後來認爲"a || a = b"應該更恰當些[/b].

果真的如此麼? 不!

一個簡單的例子可以反駁, 如果a未定義,則:

irb(main):001:0> a || a = 2
NameError: undefined local variable or method `a' for main:Object
from (irb):1
from :0


所以, [b]看來"a ||= b"正確的展開應該是: "a = b unless a"[/b]

對了麼? 還是不對!

"a ||= b"表達式最終返回的是a, 而"a = b unless a"在a非真時返回nil.

所以, [size=large]"a ||= b"的正確展開式應該爲: "if a then a else a = b end"
[/size]

想不到吧?

=== 補充 ===

鑑於此帖較長,且中間夾雜着不少因我的疏忽和錯誤造成的無謂爭論,因此把最終的到的結論列在這裏,免得看起來太累,或者有些人沒有耐心看完而增加更多的無謂的爭論。

結論如下:
1) a ||= b 不等效於: a = a || b
2) 歸納起來,準確的展開式應該是:
if defined?(a) then (a || a = b) else a = b end

特別感謝[b]lllyq[/b], 這個結論的credit應歸於他/她。

若大家發現有更好的展開形式,歡迎討論。
發佈了4 篇原創文章 · 獲贊 1 · 訪問量 2470
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章