我正在尝试编写helper_method
类型的功能。这样做我遇到了这种奇怪的行为:
irb(main):001:0> def a; end
=> :a
irb(main):002:0> b = {}
=> {}
irb(main):003:0> a(**{})
=> nil
irb(main):004:0> a(**b)
ArgumentError: wrong number of arguments (given 1, expected 0)
from (irb):1:in `a'
from (irb):4
from /home/vagrant/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
方法a
不带参数。通过splatting一个空哈希来调用它可以工作,但是这个哈希存储在一个它失败的变量中。这看起来像一个合法的错误。有人有什么想法吗?
答案 0 :(得分:0)
一些半知情的猜测:在其中使用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: -1@-1, 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: -1@-1, 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({})