Необязательные параметры Ruby

Если я определю функции Ruby следующим образом:

def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

Как я могу назвать это предоставлением только первых двух и последних аргументов? Почему не что-то вроде

ldap_get( base_dn, filter, , X)

возможно или, если возможно, как это сделать?


person Bruno Antunes    schedule 01.05.2009    source источник


Ответы (8)


В настоящее время это невозможно с рубином. Вы не можете передавать "пустые" атрибуты методам. Самое близкое, что вы можете получить, это передать nil:

ldap_get(base_dn, filter, nil, X)

Однако это установит для области значение nil, а не LDAP :: LDAP_SCOPE_SUBTREE.

Что вы можете сделать, так это установить значение по умолчанию в своем методе:

def ldap_get(base_dn, filter, scope = nil, attrs = nil)
  scope ||= LDAP::LDAP_SCOPE_SUBTREE
  ... do something ...
end

Теперь, если вы вызовете метод, как указано выше, поведение будет таким, как вы ожидаете.

person tomafro    schedule 01.05.2009
comment
Небольшая ошибка с этим методом: например, Если вы пытаетесь сделать значение по умолчанию для scope истинным и передаете false, scope ||= true не сработает. Он оценивает то же, что и nil, и устанавливает его в true - person Joshua Pinter; 10.11.2011
comment
возможно ли это с текущей версией ruby ​​через 3 года после этого ответа? - person dalloliogm; 03.06.2012
comment
@JoshPinter, хорошее объяснение. По сути, || = не a = b или c, я съежился, увидев xyz||=true. Он говорит, что если это ноль, это всегда правда. Если это правда, это правда. - person Damon Aw; 23.10.2012
comment
Когда все говорят, насколько плохо scope ||= true, я удивлен, что никто не упомянул, что лучший способ сделать это - использовать scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Конечно, даже это предполагает, что nil является недопустимым значением. - person Erik Sandberg; 29.05.2013
comment
Обновление этой старой версии: альтернативой является использование нотации подчеркивания. К сожалению, это имеет тот же эффект, что и установка параметра на nil. Некоторым может понравиться запись: ldap_get(base_dn, filter, _, X) (примечание: я не знаю (пока), когда это было введено в Ruby. Интересно SO thread). - person Eric Platon; 06.03.2014
comment
Если я правильно понимаю, проблема, поднятая Джошем, больше не является проблемой, по крайней мере, в 2.1.3+ (и, возможно, в более старых версиях). - person sealocal; 28.05.2015
comment
@EricPlaton Я думаю, что это может вызвать проблемы, если вы также используете _ для игнорирования возвращаемых значений: _, a = some_method (не уверен, насколько это стандартно для Ruby, но это распространено в других языках), потому что _ просто действует как обычная переменная. - person joelnb; 18.10.2016
comment
@sealocal, почему ты так говоришь? Я думаю, что это все еще проблема в Ruby 2.3: a = false; a ||= true; puts a - person joelnb; 18.10.2016
comment
@sprkysnrky Честно говоря, не помню и, наверное, ошибался. Думаю, я неправильно понял мысль, которую имел в виду Джош, когда я впервые нашел этот вопрос. - person sealocal; 18.10.2016

Практически всегда лучше использовать хеш опций.

def ldap_get(base_dn, filter, options = {})
  options[:scope] ||= LDAP::LDAP_SCOPE_SUBTREE
  ...
end

ldap_get(base_dn, filter, :attrs => X)
person jshen    schedule 01.05.2009
comment
Распространенная стратегия - иметь хэш опций по умолчанию и объединять все, что было передано: options = default_options.merge(options) - person Nathan Long; 13.12.2012
comment
Я не одобряю этого, потому что параметры не говорят вам, что ожидает метод или какие значения по умолчанию - person Bron Davies; 18.01.2015

Время прошло, и начиная с версии 2 Ruby поддерживает именованные параметры:

def ldap_get ( base_dn, filter, scope: "some_scope", attrs: nil )
  p attrs
end

ldap_get("first_arg", "second_arg", attrs: "attr1, attr2") # => "attr1, attr2"
person steenslag    schedule 14.07.2014
comment
Вы также можете использовать двойной знак для сбора дополнительных неопределенных аргументов ключевого слова. Это связано с этой проблемой: stackoverflow.com/a/35259850/160363 - person Henry Tseng; 08.02.2016

Это невозможно сделать так, как вы определили ldap_get. Однако, если вы определите ldap_get так:

def ldap_get ( base_dn, filter, attrs=nil, scope=LDAP::LDAP_SCOPE_SUBTREE )

Теперь вы можете:

ldap_get( base_dn, filter, X )

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

Обоснование этого простое: каждый аргумент в Ruby не обязательно должен иметь значение по умолчанию, поэтому вы не можете называть его так, как вы указали. В вашем случае, например, первые два аргумента не имеют значений по умолчанию.

person Chris Bunch    schedule 01.05.2009

1) Вы не можете перегрузить метод (Почему не поддерживает ruby перегрузка метода?) так почему бы вообще не написать новый метод?

2) Я решил аналогичную проблему с помощью оператора splat * для массива нулевой или большей длины. Затем, если я хочу передать параметр (ы), который я могу, он интерпретируется как массив, но если я хочу вызвать метод без какого-либо параметра, мне не нужно ничего передавать. См. язык программирования Ruby, страницы 186/187.

person rupweb    schedule 12.03.2014

Недавно я нашел способ обойти это. Я хотел создать метод в классе массива с необязательным параметром, чтобы сохранить или отбросить элементы в массиве.

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

class Array
  def ascii_to_text(params)
    param_len = params.length
    if param_len > 3 or param_len < 2 then raise "Invalid number of arguments #{param_len} for 2 || 3." end
    bottom  = params[0]
    top     = params[1]
    keep    = params[2]
    if keep.nil? == false
      if keep == 1
        self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
      else
        raise "Invalid option #{keep} at argument position 3 in #{p params}, must be 1 or nil"
      end
    else
      self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
    end
  end
end

Пробуем использовать наш метод класса с разными параметрами:

array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126, 1]) # Convert all ASCII values of 32-126 to their chr value otherwise keep it the same (That's what the optional 1 is for)

вывод: ["1", "2", "a", "b", "c"]

Ладно, круто, работает как задумано. Теперь давайте проверим и посмотрим, что произойдет, если мы не передадим третий параметр (1) в массив.

array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126]) # Convert all ASCII values of 32-126 to their chr value else remove it (1 isn't a parameter option)

вывод: ["a", "b", "c"]

Как вы можете видеть, третий вариант в массиве был удален, тем самым инициировав другой раздел в методе и удалив все значения ASCII, которые не находятся в нашем диапазоне (32-126)

В качестве альтернативы мы могли указать в параметрах значение nil. Это будет похоже на следующий блок кода:

def ascii_to_text(top, bottom, keep = nil)
  if keep.nil?
    self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
  else
    self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
end
person Singularity    schedule 28.01.2014

Возможно :) Просто измените определение

def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

to

def ldap_get ( base_dn, filter, *param_array, attrs=nil )
scope = param_array.first || LDAP::LDAP_SCOPE_SUBTREE

область видимости теперь будет в массиве на первом месте. Когда вы предоставляете 3 аргумента, вам будут назначены base_dn, filter и attrs, а param_array будет [] Если 4 и более аргумента, тогда param_array будет [argument1, or_more, and_more]

Обратной стороной является ... непонятное решение, действительно некрасивое. Это ответ на то, что в ruby ​​можно опустить аргумент в середине вызова функции :)

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

person m4risU    schedule 09.11.2010
comment
Это решение совершенно неверно. Использование параметра значения по умолчанию (attrs=nil) после знака (*param_array) является синтаксической ошибкой. - person Erik Sandberg; 29.05.2013
comment
-1: Эрик прав. Вызывает синтаксическую ошибку в irb 2.0.0p247. Согласно языку программирования Ruby, в Ruby 1.8 параметр splat должен был быть последним, за исключением &parameter, но в Ruby 1.9 за ним также могли следовать обычные параметры. Ни в том, ни в другом случае не было параметра с допустимым по умолчанию параметром после параметра со знаком. - person andyg0808; 19.09.2013
comment
На странице 186/187 языка программирования Ruby splat можно использовать с методами. Это должен быть последний параметр в методе, если не используется &. - person rupweb; 12.03.2014
comment
Итак, AndyG прав, порядок должен быть следующим: def ldap_get (base_dn, filter, attrs = nil, * param_array) - person rupweb; 12.03.2014

Вы можете сделать это с частичным приложением, хотя использование именованных переменных определенно приводит к более читаемому коду. В 2008 году Джон Ресиг написал в блоге статью о том, как это сделать в JavaScript: http://ejohn.org/blog/partial-functions-in-javascript/

Function.prototype.partial = function(){
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function(){
    var arg = 0;
    for ( var i = 0; i < args.length && arg < arguments.length; i++ )
      if ( args[i] === undefined )
        args[i] = arguments[arg++];
    return fn.apply(this, args);
  };
};

Вероятно, можно было бы применить тот же принцип в Ruby (за исключением прототипного наследования).

person EriF89    schedule 09.03.2016