使用可选的第一个哈希参数和keyword_args的奇数方法行为

我有以下方法:

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 `' 

我希望它将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_arg { keyword_arg: :should_be_first_param } nil

这是解析器错误还是预期的行为? 测试ruby 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: 

我希望使参数可选不会改变参数解析的方式。

我在bugs.ruby-lang.org上打开了一个问题 ,然后开发人员可以清楚它是否是这样的或者是kword args的副作用。

根据Marc-Andre Lafortune的回复,这是预期的 :

这种行为可能令人惊讶,但这是故意的。

归结为优先考虑首先填充关键字参数而不是填充未命名的参数。 这实际上是唯一可行的方法。 除其他外,请考虑以下示例:

 def foo(*rest, bar: 42) end 

如果我们不首先确定命名参数的优先级,那么在这个例子中根本没有办法为bar指定值!

所以Ruby检查:

  • 在填写所有强制性未命名参数之后
  • 如果最后剩下的参数是散列的
  • 它的所有键都是符号
  • 并且调用的方法使用关键字参数

=>然后该参数用于关键字参数。

注意键是符号的要求。 如果您使用一些不是符号的键传递哈希,这会产生更多的惊喜:

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