2016-01-07 2 views
4

У меня есть следующий метод:Странное поведение метод с дополнительным первым параметром хеш и 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 `<main>' 

Я бы необходимо установить first_param на номер { first_param: :is_hash } и keyword_arg, являющийся nil.

Кажется, что интерпретирует каждый хэш в качестве ключевого слова арг:

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.


Edit: Создание первого параметра обязательного и все работает, как я бы ожидать:

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.

+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