2016-01-07 29 views
4

我有以下方法:奇法行爲

def test(first_param = nil, keyword_arg: nil) 
    puts "first_param: #{first_param}" 
    puts "keyword_arg: #{keyword_arg}" 
end 

以下所有的電話做什麼,我希望他們做的事:

test(:something) 
#=> first_param: something 
# keyword_arg: 

test(nil, keyword_arg: :keyword_arg) 
#=> first_param: 
# keyword_arg: keyword_arg 

test({ first_param: :is_a_hash }, keyword_arg: :is_still_working) 
#=> first_param: {:first_param=>:is_a_hash} 
# keyword_arg: is_still_working 

但省略可選keyword_arg並通過散列作爲第一個參數給我一個錯誤:

test(first_param: :is_a_hash) 
#=> test.rb:1:in `test': unknown keyword: first_param (ArgumentError) 
#   from test.rb:12:in `<main>' 

我會e請將其設置爲first_param{ first_param: :is_hash }keyword_argnil

這似乎是解釋每一個散列作爲關鍵字ARG:

test(keyword_arg: :should_be_first_param) 
#=> first_param: 
# keyword_arg: should_be_first_param 

這應該已經建立first_param{ keyword_arg: :should_be_first_param },留下keyword_argnil在我看來。

這是解析器錯誤還是預期的行爲?測試紅寶石2.3.0和2.2.4。


編輯:使第一個參數強制性和一切工作像我期望:

def test_mandatory(first_param, keyword_arg: nil) 
    puts "first_param: #{first_param}" 
    puts "keyword_arg: #{keyword_arg}" 
end 

test_mandatory(first_param: :is_a_hash) 
#=> first_param: {:first_param=>:is_a_hash} 
# keyword_arg: 

test_mandatory(keyword_arg: :should_be_first_param) 
#=> first_param: {:keyword_arg=>:should_be_first_param} 
# keyword_arg: 

我期望做一個參數可選不會改變參數解析的方式。

我打開issue on bugs.ruby-lang.org,然後開發人員可以清除它是否是這樣的意圖或kword args的副作用。

+0

「*這應該已經建立'first_param'爲'{keyword_arg:should_be_first_param}',留在我看來''keyword_arg' * nil'」 - 爲什麼?你不希望它含糊不清嗎? – sawa

+0

我只給這個方法一個參數。當它不是散列時,它會設置'first_param'('test(:something)')。但是,如果參數是散列,它不會設置'first_param',而是將散列解釋爲關鍵字參數。爲什麼行爲取決於參數的類型?這是不合邏輯的(至少對我來說)。 – Markus

回答

2

它根據Marc-Andre Lafortune's reply預期:

This behavior may be surprising but it is intentional.

It boils down to giving priority to filling keyword arguments first instead of filling unnamed parameters. It is actually the only possible way to go. Among other things, think about the following example:

def foo(*rest, bar: 42) 
end 

If we don't prioritize named arguments first, then there is simply no way to specify a value for bar in this example!

So Ruby checks that:

  • after all mandatory unnamed arguments are filled
  • if the last remaining argument is hash-like
  • and all its keys are symbols
  • and the method called uses keyword arguments

=> then that parameter is used for keyword arguments.

Note the requirement on keys being symbols. This can yield even more surprising if you pass a hash with some keys that are not symbols:

def foo(a = nil, b: nil) 
    p a, b 
end 
foo(:b => 42) # => nil, 42 
foo('b' => 42) # => {"b" => 42}, nil