Когда использовать аргументы ключевого слова, также известные как именованные параметры в Ruby

Ruby 2.0.0 поддерживает аргументы ключевых слов (KA), и мне интересно, каковы преимущества/прецеденты использования этой функции в контексте чистого Ruby, особенно если рассматривать ее в свете снижения производительности из-за сопоставления ключевых слов, которое необходимо выполнять каждый раз. время вызывается метод с ключевыми аргументами.

require 'benchmark'

def foo(a:1,b:2,c:3)
  [a,b,c]
end

def bar(a,b,c)
  [a,b,c]
end

number = 1000000
Benchmark.bm(4) do |bm|
  bm.report("foo") { number.times { foo(a:7,b:8,c:9)  } }
  bm.report("bar") { number.times { bar(7,8,9) } }
end

#           user     system      total        real
# foo    2.797000   0.032000   2.829000 (  2.906362)
# bar    0.234000   0.000000   0.234000 (  0.250010)

person saihgala    schedule 25.02.2013    source источник


Ответы (4)


Аргументы с ключевыми словами имеют несколько явных преимуществ, которых никто не затронул.

Во-первых, вы не связаны с порядком аргументов. Таким образом, в случае, когда у вас иногда может быть нулевой аргумент, это выглядит намного чище:

def print_greeting(name, message = "Hello")
  puts "#{message}, #{name}"
end

print_greeting("John Connor", "Hasta la vista") 

Если вы используете аргументы ключевого слова:

def print_greeting(message: "Hello", name:)
  puts "#{message}, #{name}"
end

print_greeting(message: "Hasta la vista", name: "John Connor") 

или даже

print_greeting(name: "John Connor", message: "Goodbye")

Это устраняет необходимость запоминать порядок аргументов. Однако недостатком является то, что вы должны помнить имя аргумента. Это должно быть более или менее интуитивно понятным и, возможно, приводит к более тщательному рассмотрению сигнатур методов.

Еще одно преимущество использования аргументов с ключевыми словами заключается в том, что у вас есть метод, которому в будущем могут потребоваться дополнительные аргументы.

def create_person(name:, age:, height:)
  # make yourself some friends
end

Что, если ваши системные требования теперь должны знать о любимом шоколадном батончике человека или о том, есть ли у него лишний вес (из-за того, что он потребляет слишком много своих любимых шоколадных батончиков). Как вы можете использовать аргументы ключевых слов для этого? Простой:

def create_person(name:, age:, height:, favorite_candy:, overweight: true)
  # make yourself some fat friends
end

Перед ключевыми аргументами всегда был хеш, но это привело к гораздо большему количеству стандартного кода для извлечения и назначения переменной. Стандартный код == больше печатать == больше потенциальных опечаток == меньше времени на написание потрясающего рубинового кода.

def old_way(name, opts={})
  age    = opts[:age]
  height = opts[:height]
  # all the benefits as before, more arthritis and headaches  
end

Если вы просто настраиваете метод, который принимает один аргумент и, скорее всего, никогда не потребуется его изменять:

def say_full_name(first_name, last_name)
  puts "#{first_name} #{last_name}"
end

Тогда следует избегать аргументов с ключевыми словами, так как это незначительно влияет на производительность.

person Davey    schedule 28.01.2015
comment
Можно ли написать метод инициализатора, способный обрабатывать как именованные параметры, так и упорядоченные параметры? - person Arian Faurtosh; 15.11.2019

Поскольку KA — это инновация для всего Ruby, я вижу два основных преимущества:

  • ограничить разрешенные аргументы предопределенным набором, как это делает Rails с assert_valid_keys;
  • используйте функцию в блоках кода.

Подведение итогов:

a = lambda { |name: "Leonardo", age: 67| [name, age] }
a.call # ⇒ ["Leonardo", 67]
a.call name: "Michelangelo", age: 88 # ⇒ ["Michelangelo", 88]
a.call name: "Schwarzenegger", alive: true # ⇒ ArgumentError: unknown keyword: alive
person Aleksei Matiushkin    schedule 25.02.2013

Проблема неэффективности использования аргументов ключевого слова больше не кажется проблемой, начиная с ruby-2.2.0.

Функция №10440 устранила проблему со скоростью и была выпущена в ruby-2.2.0:

Пн Ноя 03 03:02:38 2014 Коити Сасада

  • переписать логику подбора параметров метода/блока, чтобы оптимизировать аргументы/параметры ключевого слова и аргумент знака. Функция № 10440 (подробности описаны в этой заявке)

Вы можете убедиться в этом сами (используя тот же код, что и в исходном вопросе):

(08:04:%) rvm use ruby-2.0.0-p247
Using /Users/adam/.rvm/gems/ruby-2.0.0-p247

(08:04:%) ruby keyword_benchmarks.rb

       user     system      total        real
foo    1.390000   0.060000   1.450000 (  1.451284)
bar    0.130000   0.000000   0.130000 (  0.122344)

(08:04:%)   rvm use ruby-2.2.0
Using /Users/adam/.rvm/gems/ruby-2.2.0

(08:04:%) ruby keyword_benchmarks.rb

       user     system      total        real
foo    0.140000   0.000000   0.140000 (  0.136194)
bar    0.110000   0.000000   0.110000 (  0.116246)

Использование аргументов ключевых слов по-прежнему крайне незначительно снижает производительность, но я думаю, что это приемлемый компромисс в обмен на преимущество повышенной читабельности и гибкости позиционирования.

person adamc    schedule 14.01.2016

Например

Функция

def welcome_message(message, options={})
  default_options = {name: 'hoge'}
  options = default_options.merge(options)

  "#{message}、#{options[:name]}"
end

может быть написано

def welcome_message(message, name: 'hoge')
  "#{message}、#{name}"
end
person oldergod    schedule 25.02.2013
comment
Чтобы добавить к ответу, используйте их, когда вам нужно назначить аргументы переменным внутри определения вашего метода и не писать собственный код самостоятельно. - person Kashyap; 25.02.2013
comment
Не обманывай нас, пожалуйста. def welcome_message(message, options={:name => 'hoge'}) ; "#{message}、#{options[:name]}" ; end — однострочник для welcome_message. Однако главный недостаток этого подхода заключается в том, что нужно либо расширить Hash чем-то вроде key_valid?, либо надеяться, что переданные ключи верны. Подход KA позволяет точно указать, какие ключи разрешены. - person Aleksei Matiushkin; 25.02.2013
comment
@mudasobwa Мне кажется, ты ошибаешься; с вашей функцией welcome_message("message", {:age => 22}) не будет показывать hoge в результате, но будет использовать keyword. Вы устанавливаете значение по умолчанию для всего хеша, а не значение по умолчанию для определенного ключа хеша. - person oldergod; 25.02.2013
comment
@oldergod, возможно, я не очень ясно выразился в своем вопросе. Я ищу веские причины для использования этой функции. То, что вы предоставили, больше похоже на разовый случай, что дает мне преимущество в 2 строки меньше кода и не намного больше. Например, одним явным преимуществом KA в таком языке, как C#, является просто наличие одного определения метода и отсутствие написания десятков различных сигнатур методов для реализации method overloading, тогда как в Ruby перегрузка методов не является допустимой парадигмой, поэтому это преимущество не имеет значения. Еще одно преимущество — Code Readability, относящееся к Ruby. - person saihgala; 25.02.2013