有一點半知情猜測:在其中使用doublesplat的方法調用會將doublesplat的參數與任何現有的關鍵字參數合併,並將其作爲哈希值放入最後一個參數中。隨着文字空哈希,編譯器可以看到,有沒有關鍵字,並且可以跳過創建一個哈希:
puts RubyVM::InstructionSequence.compile("b = {}; a(**{})").disasm
== disasm: #<ISeq:<compiled>@<compiled>>================================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: [email protected], kwrest: -1])
[ 2] b
0000 trace 1 ( 1)
0002 newhash 0
0004 setlocal_OP__WC__0 2
0006 putself
0007 opt_send_without_block <callinfo!mid:a, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0010 leave
你會得到相同的結果,只要關鍵字的總數是可以預見爲零。因此,這兩個編譯相同:
a()
a(**{})
隨着可變的散列(即恰好是空的),這個假設不能進行,並且合併總是被調用,生成一個散列參數:
puts RubyVM::InstructionSequence.compile("b = {}; a(**b)").disasm
== disasm: #<ISeq:<compiled>@<compiled>>================================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: [email protected], kwrest: -1])
[ 2] b
0000 trace 1 ( 1)
0002 newhash 0
0004 setlocal_OP__WC__0 2
0006 putself
0007 putspecialobject 1
0009 getlocal_OP__WC__0 2
0011 opt_send_without_block <callinfo!mid:core#hash_merge_kwd, argc:1, ARGS_SIMPLE>, <callcache>
0014 opt_send_without_block <callinfo!mid:dup, argc:0, ARGS_SIMPLE>, <callcache>
0017 opt_send_without_block <callinfo!mid:a, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0020 leave
所以我想2.2.2添加了一些優化代碼,看到**{}
的無用,並跳過生成額外的代碼。
如果你定義你的方法總是收集關鍵字的休息,也不會斷裂,因爲即使發件人沒有通過它的哈希將創建:
def a(**x); p x; end
a() # works, x is {} - hash supplied by VM at receiver
a(**b) # works, x is {} - hash supplied by sender as b.merge({})
a(**{}) # works, x is {} - hash supplied by VM at receiver, **{} ignored
a(b, **{}) # works, x is {} - hash supplied by sender, **{} ignored
a({}, **{}) # works, x is {} - hash supplied by sender, **{} ignored
a(b, **b) # works, x is {} - hash supplied by sender as b.merge(b)
a({}, **b) # works, x is {} - hash supplied by sender as b.merge({})
什麼版本的Ruby是你運行? – philomory
@philomory:2.3.1(它正好在輸出:p) – Amadan
令人驚訝的是,這在2.1.3,2.2.0中正常工作,但在2.2.2中失敗。在prev版本中,即使對於'a(** {})'也會拋出異常。猜猜這是一個錯誤,因爲我沒有看到任何文檔證明。 – prasann