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?

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.


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)

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"

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.


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


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

-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.


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).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.