Optionale Ruby-Parameter

121

Wenn ich eine Ruby-Funktion wie folgt definiere:

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

Wie kann ich es so nennen, dass es nur die ersten 2 und die letzten Argumente liefert? Warum ist so etwas nicht

ldap_get( base_dn, filter, , X)

möglich oder wenn es möglich ist, wie kann es gemacht werden?

Bruno Antunes
quelle

Antworten:

131

Dies ist mit Ruby derzeit nicht möglich. Sie können keine 'leeren' Attribute an Methoden übergeben. Das nächste, was Sie bekommen können, ist, Null zu passieren:

ldap_get(base_dn, filter, nil, X)

Dadurch wird der Bereich jedoch auf Null gesetzt, nicht auf LDAP :: LDAP_SCOPE_SUBTREE.

Sie können den Standardwert in Ihrer Methode festlegen:

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

Wenn Sie nun die Methode wie oben aufrufen, ist das Verhalten wie erwartet.

Tomafro
quelle
21
Ein kleines Problem mit dieser Methode: Wenn Sie beispielsweise versuchen, den Standardwert für scopetrue festzulegen und übergeben false, scope ||= truefunktioniert dies nicht. Es wertet das gleiche aus nilund wird es auftrue
Joshua Pinter
4
ist es mit der aktuellen Version von Ruby möglich, 3 Jahre nach dieser Antwort?
Dalloliogm
1
@ JoshPinter, nette Erklärung. Grundsätzlich ist || = nicht a = b oder c, ich habe mich beim Sehen zusammengekauert xyz||=true. Es heißt, wenn es Null ist, ist es immer wahr. Wenn es wahr ist, ist es wahr.
Damon Aw
7
Da alle sagen, wie schlimm es scope ||= trueist, bin ich überrascht, dass niemand erwähnt hat, dass der bessere Weg dies zu tun ist scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Selbst das setzt natürlich voraus, dass dies nilein ungültiger Wert ist.
Erik Sandberg
1
Update auf diesen Oldie: Eine Alternative ist die Verwendung der Unterstrichnotation. Leider hat es den gleichen Effekt wie das Einstellen des Parameters auf nil. Einige mögen vielleicht die Notation: ldap_get(base_dn, filter, _, X)(Anmerkung: Ich weiß (noch) nicht, wann dies in Ruby eingeführt wurde. Interessanter SO-Thread ).
Eric Platon
137

Sie sind fast immer besser dran, wenn Sie einen Options-Hash verwenden.

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

ldap_get(base_dn, filter, :attrs => X)
jshen
quelle
23
Eine übliche Strategie besteht darin, einen Standard-Options-Hash zu haben und alles zusammenzuführen, was übergeben wurde:options = default_options.merge(options)
Nathan Long,
7
Ich rate davon ab, da die Optionen Ihnen nicht sagen, was die Methode erwartet oder welche Standardwerte sie haben
Bron Davies
53

Die Zeit ist weitergegangen und seit Version 2 unterstützt Ruby benannte Parameter:

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
quelle
1
Sie können auch einen doppelten Splat verwenden, um zusätzliche undefinierte Schlüsselwortargumente zu sammeln. Dies hängt mit diesem Problem zusammen: stackoverflow.com/a/35259850/160363
Henry Tseng
3

Es ist nicht möglich, es so zu machen, wie Sie es definiert haben ldap_get. Wenn Sie jedoch Folgendes definieren ldap_get:

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

Jetzt kannst du:

ldap_get( base_dn, filter, X )

Aber jetzt haben Sie das Problem, dass Sie es nicht mit den ersten beiden Argumenten und dem letzten Argument aufrufen können (das gleiche Problem wie zuvor, aber jetzt ist das letzte Argument anders).

Der Grund dafür ist einfach: Jedes Argument in Ruby muss keinen Standardwert haben, daher können Sie es nicht so nennen, wie Sie es angegeben haben. In Ihrem Fall haben beispielsweise die ersten beiden Argumente keine Standardwerte.

Chris Bunch
quelle
1

1) Sie können die Methode nicht überladen ( Warum wird die Ruby-Methode nicht überladen? ). Warum also nicht eine neue Methode schreiben?

2) Ich habe ein ähnliches Problem mit dem Splat-Operator * für ein Array mit einer Länge von null oder mehr gelöst. Wenn ich dann einen oder mehrere Parameter übergeben möchte, wird dieser als Array interpretiert. Wenn ich die Methode jedoch ohne Parameter aufrufen möchte, muss ich nichts übergeben. Siehe Ruby Programming Language- Seiten 186/187

Rupweb
quelle
0

Kürzlich habe ich einen Weg gefunden, dies zu umgehen. Ich wollte eine Methode in der Array-Klasse mit einem optionalen Parameter erstellen, um Elemente im Array beizubehalten oder zu verwerfen.

Ich habe dies simuliert, indem ich ein Array als Parameter übergeben und dann überprüft habe, ob der Wert an diesem Index Null war oder nicht.

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

Probieren Sie unsere Klassenmethode mit verschiedenen Parametern aus:

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)

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

Okay, cool, das funktioniert wie geplant. Lassen Sie uns nun überprüfen, was passiert, wenn wir die dritte Parameteroption (1) im Array nicht übergeben.

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)

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

Wie Sie sehen können, wurde die dritte Option im Array entfernt, wodurch ein anderer Abschnitt in der Methode eingeleitet und alle ASCII-Werte entfernt wurden, die nicht in unserem Bereich liegen (32-126).

Alternativ hätten wir den Wert in den Parametern als Null ausgeben können. Welches würde ähnlich wie der folgende Codeblock aussehen:

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
Singularität
quelle
-1

Es ist möglich :) Ändern Sie einfach die Definition

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

zu

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

Der Bereich wird nun an erster Stelle im Array sein. Wenn Sie 3 Argumente angeben, haben Sie base_dn, filter und attrs zugewiesen, und param_array ist []. Wenn 4 und mehr Argumente vorhanden sind, ist param_array [argument1, or_more und_more].

Nachteil ist ... es ist eine unklare Lösung, wirklich hässlich. Dies ist zu beantworten, dass es möglich ist, Argumente in der Mitte des Funktionsaufrufs in Ruby auszulassen :)

Eine andere Sache, die Sie tun müssen, ist, den Standardwert des Bereichs neu zu schreiben.

m4risU
quelle
4
Diese Lösung ist völlig falsch. Es ist ein Syntaxfehler, attrs=nilnach einem splat ( *param_array) einen Standardwertparameter ( ) zu verwenden.
Erik Sandberg
3
-1: Erik ist richtig. Verursacht einen Syntaxfehler in irb 2.0.0p247. Laut The Ruby Programming Language musste der Splat-Parameter in Ruby 1.8 bis auf ein letzter sein &parameter, in Ruby 1.9 konnten jedoch auch "normale Parameter" folgen. In keinem Fall war ein Parameter mit einem Standardwert nach einem Parameter mit einem Splat zulässig.
andyg0808
Ruby Programming Language Seite 186/187 Der Splat kann problemlos mit Methoden verwendet werden. Es muss der letzte Parameter in der Methode sein, es sei denn, & wird verwendet.
Rupweb
AndyG hat also Recht, die Reihenfolge muss lauten: def ldap_get (base_dn, filter, attrs = nil, * param_array)
rupweb
-1

Sie können dies mit einer Teilanwendung tun, obwohl die Verwendung benannter Variablen definitiv zu besser lesbarem Code führt. John Resig schrieb 2008 einen Blog-Artikel darüber, wie man das in JavaScript macht: 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);
  };
};

Es wäre wahrscheinlich möglich, dasselbe Prinzip in Ruby anzuwenden (mit Ausnahme der prototypischen Vererbung).

EriF89
quelle