Parâmetros opcionais do Ruby

121

Se eu definir um Ruby funciona assim:

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

Como posso chamá-lo de fornecer apenas os dois primeiros e os últimos argumentos? Por que não é algo como

ldap_get( base_dn, filter, , X)

possível ou se for possível, como isso pode ser feito?

Bruno Antunes
fonte

Respostas:

131

Isso não é possível com o ruby ​​atualmente. Você não pode passar atributos 'vazios' para métodos. O mais próximo que você pode chegar é passar zero:

ldap_get(base_dn, filter, nil, X)

No entanto, isso definirá o escopo para nulo, não para LDAP :: LDAP_SCOPE_SUBTREE.

O que você pode fazer é definir o valor padrão dentro do seu método:

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

Agora, se você chamar o método como acima, o comportamento será o esperado.

tomafro
fonte
21
Um pouco de dificuldade com este método: por exemplo, se você está tentando definir o valor padrão como scopeverdadeiro e passa false, scope ||= truenão funcionará. Ele avalia o mesmo nile o definirá comotrue
Joshua Pinter
4
é possível com a versão atual do ruby, 3 anos após esta resposta?
precisa saber é o seguinte
1
@ Joshinter, boa explicação. Basicamente || = não é a = b ou c, eu me encolhi ao ver xyz||=true. Está dizendo que se é nulo, é sempre verdade. Se é verdade, é verdade.
Damon Aw
7
Com todo mundo dizendo o quão ruim scope ||= trueé, fico surpreso que ninguém tenha mencionado que a melhor maneira de fazer isso é scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Obviamente, mesmo isso assumindo que nilé um valor inválido.
Erik Sandberg
1
Atualização para este oldie: Uma alternativa é usar a notação de sublinhado. Infelizmente, tem o mesmo efeito que definir o parâmetro para nil. Alguns podem gostar da notação: ldap_get(base_dn, filter, _, X)(nota: eu ainda não sei) quando isso foi introduzido no Ruby. Segmento SO interessante ).
precisa
137

Você quase sempre está melhor usando um hash de opções.

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

ldap_get(base_dn, filter, :attrs => X)
jshen
fonte
23
Uma estratégia comum é ter um hash de opções padrão e mesclar o que foi passado:options = default_options.merge(options)
Nathan Long
7
Eu desencorajo isso porque as opções não informam o que o método espera ou quais são os valores padrão.
Bron Davies
53

O tempo mudou e, desde a versão 2, o Ruby suporta parâmetros nomeados:

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"
steenslag
fonte
1
Você também pode usar um splat duplo para coletar argumentos de palavras-chave indefinidas adicionais. Isto está relacionado com esta questão: stackoverflow.com/a/35259850/160363
Henry Tseng
3

Não é possível fazer isso da maneira que você definiu ldap_get. No entanto, se você definir ldap_getassim:

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

Agora você pode:

ldap_get( base_dn, filter, X )

Mas agora você tem um problema que não pode chamar com os dois primeiros argumentos e o último argumento (o mesmo problema de antes, mas agora o último argumento é diferente).

A lógica para isso é simples: todos os argumentos do Ruby não precisam ter um valor padrão, portanto você não pode chamá-lo da maneira que especificou. No seu caso, por exemplo, os dois primeiros argumentos não têm valores padrão.

Chris Bunch
fonte
1

1) Você não pode sobrecarregar o método ( por que o ruby ​​não suporta sobrecarga de método? ); Por que não escrever um novo método completamente?

2) Resolvi um problema semelhante usando o operador splat * para uma matriz de zero ou mais comprimentos. Então, se eu quiser passar um parâmetro, posso interpretá-lo como uma matriz, mas se eu quiser chamar o método sem nenhum parâmetro, não preciso passar nada. Consulte Ruby Programming Language, páginas 186/187

rupweb
fonte
0

Recentemente eu encontrei uma maneira de contornar isso. Eu queria criar um método na classe array com um parâmetro opcional, para manter ou descartar elementos no array.

A maneira como simulei isso foi passando uma matriz como parâmetro e depois verificando se o valor naquele índice era nulo ou não.

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

Experimentando nosso método de classe com diferentes parâmetros:

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)

resultado: ["1", "2", "a", "b", "c"]

Ok, legal, que funciona como planejado. Agora vamos verificar e ver o que acontece se não passarmos a terceira opção de parâmetro (1) na matriz.

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)

resultado: ["a", "b", "c"]

Como você pode ver, a terceira opção na matriz foi removida, iniciando uma seção diferente no método e removendo todos os valores ASCII que não estão em nosso intervalo (32-126)

Como alternativa, poderíamos ter emitido o valor nulo nos parâmetros. O que seria semelhante ao seguinte bloco de código:

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
Singularidade
fonte
-1

É possível :) Basta alterar a definição

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

para

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

o escopo estará agora em ordem em primeiro lugar. Quando você fornece 3 argumentos, você atribui base_dn, filter e attrs e param_array será [] Quando 4 ou mais argumentos, então param_array será [argument1, or_more e_more]

A desvantagem é ... não é uma solução clara, muito feia. Isso é para responder que é possível omitir argumentos no meio da chamada de função em ruby ​​:)

Outra coisa que você precisa fazer é reescrever o valor padrão do escopo.

m4risU
fonte
4
Esta solução está completamente errada. É um erro de sintaxe usar um parâmetro de valor padrão ( attrs=nil) após um splat ( *param_array).
Erik Sandberg
3
-1: Erik está correto. Causa um erro de sintaxe no irb 2.0.0p247. De acordo com a linguagem de programação Ruby , no Ruby 1.8 o parâmetro splat tinha que ser o último, exceto um &parameter, mas no Ruby 1.9, ele também podia ser seguido por "parâmetros comuns". Em nenhum dos casos houve um parâmetro com um padrão legal depois de um parâmetro com um splat.
precisa saber é o seguinte
Linguagem de programação Ruby página 186/187 o splat é bom para usar com métodos. Ele deve ser o último parâmetro no método, a menos que & seja usado.
rupweb
Então AndyG é certo, as necessidades a fim de ser: ldap_get def (base_dn, filtro, attrs = nil, * param_array)
rupweb
-1

Você pode fazer isso com aplicação parcial, embora o uso de variáveis ​​nomeadas definitivamente leve a um código mais legível. John Resig escreveu um artigo de blog em 2008 sobre como fazê-lo em 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);
  };
};

Provavelmente seria possível aplicar o mesmo princípio em Ruby (exceto a herança prototípica).

EriF89
fonte