2009-05-01 2 views
115

Если я определяю функции Ruby, как это:Рубин необязательные параметры

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

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

ldap_get(base_dn, filter, , X) 

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

ответ

122

В настоящий момент это невозможно с рубином. Вы не можете передавать «пустые» атрибуты методам. Ближайший вы можете получить это передать ноль:

ldap_get(base_dn, filter, nil, X) 

Однако, это установит область к нулю, а не LDAP :: LDAP_SCOPE_SUBTREE.

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

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

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

+21

Немного gotcha с этим методом: например. Если вы пытаетесь сделать значение по умолчанию для 'scope' true, и вы переходите в' false', 'scope || = true' не будет работать. Он оценивает то же самое, что и «nil» и устанавливает значение «true» –

+4

, возможно ли это с текущей версией ruby, через 3 года после этого ответа? – dalloliogm

+1

@JoshPinter, приятный информация. В основном || = не является a = b или c, я съежился, увидев «xyz || = true». Это говорит, что это нуль, это всегда так. Если это правда, это правда. –

3

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

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

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

ldap_get(base_dn, filter, X) 

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

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

134

Вы почти всегда лучше используете хэш параметров.

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

ldap_get(base_dn, filter, :attrs => X) 
+23

Общая стратегия состоит в том, чтобы иметь хэш хэш по умолчанию и объединить все, что было передано: 'options = default_options.merge (options)' –

+4

Я отговариваю это, потому что параметры не говорят вам, что ожидает этот метод или какие значения по умолчанию –

0

Это возможно :) Просто измените

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

в

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

объем теперь будет в массиве на своем первом месте. При предоставлении 3-х аргументов, то вы назначили base_dn, фильтр и ATTRS и param_array будет [] При 4 и более аргументов, то param_array будет [argument1, or_more, and_more]

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

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

+3

Это решение совершенно неверно. Синтаксическая ошибка для использования параметра значения по умолчанию ('attrs = nil') после символа splash (' * param_array'). –

+3

-1: Эрик прав. Вызывает синтаксическую ошибку в irb 2.0.0p247. Согласно «The Ruby Programming Language_», в Ruby 1.8 параметр splat должен был быть последним, за исключением «& parameter», но в Ruby 1.9 вместо него могут следовать «обычные параметры». В любом случае не был параметром с правом по умолчанию после параметра с символом splat. – andyg0808

+0

Язык программирования Ruby, стр. 186/187, splat отлично подходит для использования с методами. Он должен быть последним параметром метода, если не используется &. – rupweb

0

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

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

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 
1

1) Вы не можете перегрузить метод (Why doesn't ruby support method overloading?), так почему бы не написать новый метод в целом?

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

44

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

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" 
+0

Вы также можете использовать двойной знак для сбора дополнительных неопределенных аргументов ключевого слова. Это связано с этой проблемой: http://stackoverflow.com/a/35259850/160363 –

0

Вы могли бы сделать это с частичным применением, хотя использование именованных переменных определенно приводит к более читаемый код. Резиг написал статью в блоге в 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); 
    }; 
}; 

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

Смежные вопросы