Есть ли идиоматический способ указать значения по умолчанию для необязательных параметров в Ruby?

Есть ли более краткий и идиоматический способ написать следующий код, который используется для указания значений по умолчанию для необязательных параметров (в хэше params/options) для метода?

def initialize(params={})
  if params.has_key? :verbose
    @verbose = params[:verbose]
  else
    @verbose = true # this is the  default value
  end
end

Я хотел бы упростить его до чего-то вроде этого:

def initialize(params={})
  @verbose = params[:verbose] or true
end

который почти работает, за исключением того, что вам действительно нужно использовать has_key? :verbose в качестве условия, а не просто вычислять params[:verbose], чтобы охватить случаи, когда вы хотите указать значение «false» (т. е. если вы хотите для передачи :verbose => false в качестве аргумента в этом примере).

Я понимаю, что в этом простом примере я мог бы легко сделать:

def initialize(verbose=false)
  @verbose = verbose
end

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


person Kelan    schedule 29.12.2009    source источник
comment
В Perl теперь есть синтаксис //=, чтобы таким образом обойти проблему проверки ложных значений. Я знаю, что это не помогает для этого. Вот почему это комментарий, а не ответ...   -  person mopoke    schedule 29.12.2009


Ответы (4)


Обычный шаблон заключается в использовании

def foo(options = {})
  options = { :default => :value }.merge(options)
end

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

person Ciarán Walsh    schedule 29.12.2009
comment
есть ли причина использовать простой merge вместо merge! ? (поскольку я предполагаю, что merge! будет быстрее) - person horseyguy; 29.12.2009
comment
@banister: использование options.merge!(defaults) приведет к перезаписи в неправильном направлении. Вам нужно сделать defaults.merge(options), чтобы перезаписать значения по умолчанию указанными параметрами. И, поскольку было бы неудобно сохранять результат в «значениях по умолчанию», вы не можете использовать merge! и вместо этого должны назначить результат обратно в «параметры». Я не уверен, есть ли огромная разница в скорости между merge и merge!. - person Kelan; 24.02.2010
comment
@Келан, что? код эквивалентен эквивалентен defaults.merge(options), поскольку { :default => :value } равен defaults (хотя и встроенный). Если вы посмотрите на код, то увидите, что он использует options как для параметра, так и для конечного хеша. Если не верите мне, попробуйте. Использование merge! прекрасно (я только что сделал, и это работает именно так, как вы ожидаете). - person horseyguy; 24.02.2010
comment
@banister: О, ты хотел сделать options = defaults.merge!(options) вместо options = defaults.merge(options)? Да, это работает нормально. Раньше я думал, что вы имели в виду просто defaults.merge!(options) и не возвращать результат к опциям... Итак, я согласен, что любой способ работает. Что касается производительности, на основе небольшого теста, который я только что провел, использование merge! действительно примерно на 40% быстрее. Значит, ваше предположение было верным. Хороший звонок! - person Kelan; 24.02.2010


Я думаю, ты ищешь это

params = { :verbose => true }.merge(params)
person Daniel    schedule 29.12.2009

Другой способ написать это, более кратко, был бы

def foo(options = {})
    options.reverse_merge! value1: true, value2: 100
end

Это устанавливает для options[:value1] значение true (значение по умолчанию), если переданные параметры уже не содержат ключ :value1. То же самое для :value2

person Zack Xu    schedule 01.11.2013
comment
Чтобы было ясно, reverse_merge!()reverse_merge()) специфичны для Rails (ActiveSupport) и, следовательно, недоступны в чистом Ruby. - person mwp; 27.10.2015