Parámetros opcionales de Ruby

121

Si defino un Ruby funciona así:

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

¿Cómo puedo llamarlo suministrando solo los primeros 2 y los últimos args? ¿Por qué no es algo como

ldap_get( base_dn, filter, , X)

posible o si es posible, ¿cómo se puede hacer?

Bruno Antunes
fuente

Respuestas:

131

Esto no es posible con ruby ​​actualmente. No puede pasar atributos 'vacíos' a los métodos. Lo más cercano que puede llegar es pasar nulo:

ldap_get(base_dn, filter, nil, X)

Sin embargo, esto establecerá el alcance en nulo, no LDAP :: LDAP_SCOPE_SUBTREE.

Lo que puede hacer es establecer el valor predeterminado dentro de su método:

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

Ahora, si llama al método anterior, el comportamiento será el esperado.

tomafro
fuente
21
Un pequeño inconveniente con este método: por ejemplo, si está tratando de hacer que el valor predeterminado sea scopeverdadero y lo transfiera false, scope ||= trueno funcionará. Evalúa lo mismo nily lo configurará comotrue
Joshua Pinter
44
¿Es posible con la versión actual de ruby, 3 años después de esta respuesta?
dalloliogm
1
@JoshPinter, buena explicación. Básicamente || = no es a = b o c, me estremecí al ver xyz||=true. Dice que si es nulo, siempre es cierto. Si es verdad, es verdad.
Damon Aw
77
Con todos diciendo lo malo que scope ||= truees, me sorprende que nadie haya mencionado que la mejor manera de hacerlo es con scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Por supuesto, incluso eso supone que niles un valor no válido.
Erik Sandberg
1
Actualización de este viejo: una alternativa es utilizar la notación de subrayado. Desafortunadamente, tiene el mismo efecto que establecer el parámetro en nil. A algunos les puede gustar la notación: ldap_get(base_dn, filter, _, X)(nota: no sé (todavía) cuándo se introdujo en Ruby. Interesante hilo SO ).
Eric Platon
137

Casi siempre es mejor usar un hash de opciones.

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

ldap_get(base_dn, filter, :attrs => X)
jshen
fuente
23
Una estrategia común es tener un hash de opciones predeterminadas y fusionarse en lo que se haya aprobado:options = default_options.merge(options)
Nathan Long
77
Desaliento esto porque las opciones no le dicen qué espera el método o cuáles son los valores predeterminados
Bron Davies
53

El tiempo pasó y, desde la versión 2, Ruby admite parámetros con nombre:

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
fuente
1
También puede usar un símbolo doble para recopilar argumentos de palabras clave indefinidos adicionales. Esto está relacionado con este problema: stackoverflow.com/a/35259850/160363
Henry Tseng
3

No es posible hacerlo de la manera que has definido ldap_get. Sin embargo, si define ldap_getasí:

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

Ahora usted puede:

ldap_get( base_dn, filter, X )

Pero ahora tiene un problema que no puede llamarlo con los dos primeros argumentos y el último argumento (el mismo problema que antes pero ahora el último argumento es diferente).

La razón de esto es simple: no se requiere que cada argumento en Ruby tenga un valor predeterminado, por lo que no puede llamarlo de la manera que ha especificado. En su caso, por ejemplo, los dos primeros argumentos no tienen valores predeterminados.

Chris Bunch
fuente
1

1) No puede sobrecargar el método ( ¿por qué Ruby no admite la sobrecarga del método? ) Entonces, ¿ por qué no escribir un método nuevo por completo?

2) Resolví un problema similar usando el operador splat * para una matriz de longitud cero o más. Entonces, si quiero pasar un parámetro o parámetros que puedo, se interpreta como una matriz, pero si quiero llamar al método sin ningún parámetro, entonces no tengo que pasar nada. Consulte las páginas 186/187 del lenguaje de programación Ruby .

rupweb
fuente
0

Recientemente encontré una forma de evitar esto. Quería crear un método en la clase de matriz con un parámetro opcional, para mantener o descartar elementos en la matriz.

La forma en que simulé esto fue pasando una matriz como parámetro y luego comprobando si el valor en ese índice era nulo o no.

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

Probar nuestro método de clase con 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)

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

Bien, genial, eso funciona según lo planeado. Ahora revisemos y veamos qué sucede si no pasamos la opción del tercer parámetro (1) en la 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)

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

Como puede ver, la tercera opción en la matriz se ha eliminado, iniciando así una sección diferente en el método y eliminando todos los valores ASCII que no están en nuestro rango (32-126)

Alternativamente, podríamos haber emitido el valor como nulo en los parámetros. Que se vería similar al siguiente bloque 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
Singularidad
fuente
-1

Es posible :) Simplemente cambie la definición

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

a

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

alcance estará ahora en matriz en su primer lugar. Cuando proporcione 3 argumentos, entonces habrá asignado base_dn, filter y attrs y param_array será [] Cuando 4 y más argumentos entonces param_array será [argumento1, or_more, y_more]

Lo malo es ... no es una solución clara, realmente fea. Esto es para responder que es posible omitir argumentos en el medio de la llamada a la función en ruby ​​:)

Otra cosa que debe hacer es reescribir el valor predeterminado del alcance.

m4risU
fuente
44
Esta solución es completamente incorrecta. Es un error de sintaxis utilizar un parámetro de valor predeterminado ( attrs=nil) después de un splat ( *param_array).
Erik Sandberg
3
-1: Erik está en lo correcto. Provoca un error de sintaxis en irb 2.0.0p247. De acuerdo con el lenguaje de programación de Ruby , en Ruby 1.8 el parámetro splat tenía que ser el último excepto un &parameter, pero en Ruby 1.9 también podía ser seguido por "parámetros ordinarios". En ninguno de los casos fue un parámetro con un valor legal predeterminado después de un parámetro con un splat.
andyg0808
Lenguaje de programación Ruby, página 186/187, el splat está bien para usar con métodos. Tiene que ser el último parámetro en el método a menos que se use &.
rupweb
Entonces AndyG tiene razón, el orden debe ser: def ldap_get (base_dn, filter, attrs = nil, * param_array)
rupweb
-1

Podría hacerlo con una aplicación parcial, aunque el uso de variables con nombre definitivamente conduce a un código más legible. John Resig escribió un artículo de blog en 2008 sobre cómo hacerlo en 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);
  };
};

Probablemente sería posible aplicar el mismo principio en Ruby (a excepción de la herencia prototípica).

EriF89
fuente