2016-11-15 5 views
1

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は引数を取りません。そうすることで、私はこの奇妙な振る舞いを検出しました。空のハッシュをスプラットして呼び出すことはできますが、そのハッシュは失敗した変数に格納されています。これは正当なバグのようです。誰にでもアイデアはありますか?

+0

あなたはルビーのどのバージョンランニング? – philomory

+0

@philomory:2.3.1(これは出力にあります:p) – Amadan

+0

驚いたことに、これは2.1.3,2.2.0では問題なく動作しますが、2.2.2では失敗します。それ以前のバージョンでは、 'a(** {}) 'でも例外をスローします。私はこれについての証拠は何も見当たりませんので、バグだと思います。 – prasann

答えて

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: [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    

あなたがいる限り、キーワードの総数は予想通りゼロであるのと同じ結果が得られます。このように、これらの2つは同じようにコンパイル:変数のハッシュ(つまり空であることを起こる)で

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({}) 
関連する問題