Parametri opzionali di Ruby


121

Se definisco un Ruby funziona in questo modo:

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

Come posso chiamarlo fornendo solo i primi 2 e gli ultimi argomenti? Perché non è qualcosa di simile

ldap_get( base_dn, filter, , X)

possibile o se è possibile, come si può fare?

Risposte:


131

Questo non è possibile con Ruby attualmente. Non puoi passare attributi "vuoti" ai metodi. Il più vicino che puoi ottenere è passare zero:

ldap_get(base_dn, filter, nil, X)

Tuttavia, questo imposterà l'ambito su nil, non su LDAP :: LDAP_SCOPE_SUBTREE.

Quello che puoi fare è impostare il valore predefinito all'interno del tuo metodo:

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

Ora se chiami il metodo come sopra, il comportamento sarà come ti aspetti.


21
Un piccolo trucco con questo metodo: ad esempio, se stai cercando di impostare il valore predefinito per scopevero e passi false, scope ||= truenon funzionerà. Valuta lo stesso di nile lo imposterà sutrue
Joshua Pinter

4
è possibile con l'attuale versione di ruby, 3 anni dopo questa risposta?
dalloliogm

1
@ JoshPinter, bella spiegazione. Fondamentalmente || = non è a = b o c, mi sono fatto piccolo nel vedere xyz||=true. Sta dicendo che se è zero, è sempre vero. Se è vero, è vero.
Damon Aw

7
Con tutti che dicono quanto sia brutto scope ||= true, sono sorpreso che nessuno abbia detto che il modo migliore per farlo è con scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Naturalmente, anche questo presuppone che nilsia un valore non valido.
Erik Sandberg

1
Aggiornamento a questo vecchio: un'alternativa è usare la notazione di sottolineatura. Sfortunatamente, ha lo stesso effetto dell'impostazione del parametro su nil. Ad alcuni potrebbe piacere la notazione: ldap_get(base_dn, filter, _, X)(nota: non so (ancora) quando è stato introdotto in Ruby. Interessante thread SO ).
Eric Platon

137

Stai quasi sempre meglio usando un hash delle opzioni.

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

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

23
Una strategia comune è avere un hash delle opzioni predefinito e unire tutto ciò che è stato passato:options = default_options.merge(options)
Nathan Long,

7
Lo sconsiglio perché le opzioni non ti dicono cosa si aspetta il metodo o quali sono i valori predefiniti
Bron Davies

53

Il tempo è passato e dalla versione 2 Ruby supporta parametri denominati:

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
È inoltre possibile utilizzare un doppio simbolo per raccogliere ulteriori argomenti di parole chiave non definite. Ciò è correlato a questo problema: stackoverflow.com/a/35259850/160363
Henry Tseng

3

Non è possibile farlo nel modo che hai definito ldap_get. Tuttavia, se definisci in ldap_getquesto modo:

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

Ora puoi:

ldap_get( base_dn, filter, X )

Ma ora hai il problema di non poterlo chiamare con i primi due arg e l'ultimo arg (lo stesso problema di prima ma ora l'ultimo arg è diverso).

La logica di ciò è semplice: non è necessario che ogni argomento in Ruby abbia un valore predefinito, quindi non puoi chiamarlo nel modo in cui hai specificato. Nel tuo caso, ad esempio, i primi due argomenti non hanno valori predefiniti.


1

1) Non è possibile sovraccaricare il metodo ( perché ruby ​​non supporta il sovraccarico del metodo? ) Quindi perché non scrivere un nuovo metodo del tutto?

2) Ho risolto un problema simile usando l'operatore splat * per un array di zero o più lunghezza. Quindi, se voglio passare un parametro (i) posso, viene interpretato come un array, ma se voglio chiamare il metodo senza alcun parametro, non devo passare nulla. Vedere Ruby Programming Language alle pagine 186/187


0

Recentemente ho trovato un modo per aggirare questo problema. Volevo creare un metodo nella classe array con un parametro opzionale, per mantenere o scartare gli elementi nell'array.

Il modo in cui l'ho simulato è stato passando un array come parametro e quindi controllando se il valore in quell'indice era nullo o meno.

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

Provando il nostro metodo di classe con diversi parametri:

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)

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

Va bene, va bene, funziona come previsto. Ora controlliamo e vediamo cosa succede se non passiamo la terza opzione parametro (1) nell'array.

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)

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

Come puoi vedere, la terza opzione nell'array è stata rimossa, iniziando così una sezione diversa nel metodo e rimuovendo tutti i valori ASCII che non sono nel nostro intervallo (32-126)

In alternativa, avremmo potuto emettere il valore come nil nei parametri. Che sarebbe simile al seguente blocco di codice:

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

È possibile :) Basta cambiare la definizione

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

per

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

l'ambito sarà ora in matrice al primo posto. Quando fornisci 3 argomenti, allora avrai assegnato base_dn, filtro e attrs e param_array sarà [] Quando 4 e più argomenti, param_array sarà [argomento1, or_more e_more]

Il rovescio della medaglia è ... non è una soluzione chiara, davvero brutta. Questo per rispondere che è possibile omettere l'argomento nel mezzo della chiamata di funzione in ruby ​​:)

Un'altra cosa che devi fare è riscrivere il valore predefinito di scope.


4
Questa soluzione è completamente sbagliata. È un errore di sintassi utilizzare un parametro di valore predefinito ( attrs=nil) dopo uno splat ( *param_array).
Erik Sandberg

3
-1: Erik ha ragione. Causa un errore di sintassi in irb 2.0.0p247. Secondo The Ruby Programming Language , in Ruby 1.8 il parametro splat doveva essere l'ultimo ad eccezione di un &parameter, ma in Ruby 1.9 poteva essere seguito anche da "parametri ordinari". In nessuno dei due casi un parametro con un valore predefinito era legale dopo un parametro con uno splat.
andyg0808

Ruby Programming Language pagina 186/187 lo splat va bene da usare con i metodi. Deve essere l'ultimo parametro nel metodo a meno che non venga utilizzato &.
rupweb

Quindi AndyG ha ragione, l'ordine deve essere: def ldap_get (base_dn, filter, attrs = nil, * param_array)
rupweb

-1

Puoi farlo con un'applicazione parziale, anche se l'uso di variabili denominate porta sicuramente a un codice più leggibile. John Resig ha scritto un articolo sul blog nel 2008 su come farlo in 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);
  };
};

Probabilmente sarebbe possibile applicare lo stesso principio in Ruby (eccetto per l'eredità prototipale).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.