有一点半知情猜测:在其中使用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