繁体   English   中英

为不带参数的方法splatting哈希时的不同行为

[英]different behavior when splatting a hash for a method that takes not arguments

我正在尝试编写一个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一个空哈希来调用它可以工作,但是这个哈希存储在一个它失败的变量中。 这看起来像一个合法的错误。 有人有什么想法吗?

一些半知情的猜测:在其中使用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({})

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM