Opcjonalne parametry języka Ruby

121

Jeśli zdefiniuję funkcje Rubiego w ten sposób:

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

Jak mogę to nazwać dostarczaniem tylko pierwszych 2 i ostatnich argumentów? Dlaczego coś takiego nie jest

ldap_get( base_dn, filter, , X)

jest możliwe, a jeśli to możliwe, jak to zrobić?

Bruno Antunes
źródło

Odpowiedzi:

131

Obecnie nie jest to możliwe w przypadku ruby. Nie możesz przekazywać „pustych” atrybutów do metod. Najbliższe, co możesz uzyskać, to zdać zero:

ldap_get(base_dn, filter, nil, X)

Jednak spowoduje to ustawienie zakresu na zero, a nie LDAP :: LDAP_SCOPE_SUBTREE.

Możesz ustawić wartość domyślną w swojej metodzie:

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

Teraz, jeśli wywołasz metodę jak powyżej, zachowanie będzie zgodne z oczekiwaniami.

tomafro
źródło
21
Trochę kłopotów z tą metodą: np. Jeśli próbujesz ustawić domyślną wartość scopetrue i przepuszczasz false, scope ||= truenie zadziała. Ocenia to samo, co nili ustawi natrue
Joshua Pinter
4
czy jest to możliwe z obecną wersją ruby, 3 lata po tej odpowiedzi?
dalloliogm
1
@JoshPinter, ładne wyjaśnienie. Zasadniczo || = to nie a = b lub c, wzdrygnąłem się na widok xyz||=true. Mówi, że jeśli jest zero, to zawsze prawda. Jeśli to prawda, to prawda.
Damon Aw,
7
Ponieważ wszyscy mówią, jak źle scope ||= truejest, jestem zaskoczony, że nikt nie wspomniał, że lepszym sposobem na to jest użycie scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Oczywiście nawet przy założeniu, że niljest to nieprawidłowa wartość.
Erik Sandberg
1
Zaktualizuj do tego starego: alternatywą jest użycie notacji podkreślenia. Niestety ma to taki sam skutek, jak ustawienie parametru na nil. Niektórym może się spodobać notacja: ldap_get(base_dn, filter, _, X)(uwaga: nie wiem (jeszcze) kiedy to zostało wprowadzone w Rubim. Ciekawy wątek SO ).
Eric Platon
137

Prawie zawsze lepiej jest używać skrótu opcji.

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

ldap_get(base_dn, filter, :attrs => X)
jshen
źródło
23
Typową strategią jest posiadanie domyślnego skrótu opcji i scalanie tego, co zostało przekazane w:options = default_options.merge(options)
Nathan Long
7
Odradzam to, ponieważ opcje nie mówią ci, czego metoda oczekuje ani jakie są wartości domyślne
Bron Davies
53

Czas minął i od wersji 2 Ruby obsługuje nazwane parametry:

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
źródło
1
Możesz również użyć podwójnej ikony, aby zebrać dodatkowe niezdefiniowane argumenty słów kluczowych. Jest to związane z tym problemem: stackoverflow.com/a/35259850/160363
Henry Tseng
3

Nie można tego zrobić tak, jak zdefiniowałeś ldap_get. Jeśli jednak zdefiniujesz w ldap_getten sposób:

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

Teraz możesz:

ldap_get( base_dn, filter, X )

Ale teraz masz problem, że nie możesz go wywołać z dwoma pierwszymi argumentami i ostatnim argumentem (ten sam problem co wcześniej, ale teraz ostatni argument jest inny).

Przyczyna tego jest prosta: każdy argument w Rubim nie musi mieć wartości domyślnej, więc nie można go nazwać w sposób określony przez użytkownika. Na przykład w twoim przypadku pierwsze dwa argumenty nie mają wartości domyślnych.

Chris Bunch
źródło
1

1) Nie możesz przeciążać metody ( dlaczego ruby ​​nie obsługuje przeciążania metod? ), Więc dlaczego nie napisać zupełnie nowej metody?

2) Podobny problem rozwiązałem używając operatora splat * dla tablicy o zerowej lub większej długości. Następnie, jeśli chcę przekazać parametr (y), który mogę, jest on interpretowany jako tablica, ale jeśli chcę wywołać metodę bez żadnego parametru, nie muszę niczego przekazywać. Patrz język programowania Ruby na stronach 186/187

rupweb
źródło
0

Ostatnio znalazłem sposób na obejście tego. Chciałem stworzyć metodę w klasie tablicy z opcjonalnym parametrem, aby zachować lub odrzucić elementy w tablicy.

Sposób, w jaki to zasymulowałem, polegał na przekazaniu tablicy jako parametru, a następnie sprawdzeniu, czy wartość w tym indeksie wynosi zero, czy nie.

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

Wypróbuj naszą metodę klasową z różnymi parametrami:

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)

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

Dobra, fajnie, działa zgodnie z planem. Teraz sprawdźmy i zobaczmy, co się stanie, jeśli nie przekażemy trzeciej opcji parametru (1) w tablicy.

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)

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

Jak widać, trzecia opcja w tablicy została usunięta, inicjując w ten sposób inną sekcję w metodzie i usuwając wszystkie wartości ASCII, które nie znajdują się w naszym zakresie (32-126)

Alternatywnie moglibyśmy ustawić wartość jako zero w parametrach. Który wyglądałby podobnie do następującego bloku kodu:

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
Osobliwość
źródło
-1

Jest to możliwe :) Wystarczy zmienić definicję

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

do

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

zakres będzie teraz w tablicy na pierwszym miejscu. Kiedy podasz 3 argumenty, to przypiszesz base_dn, filter i attrs, a param_array będzie [] Gdy 4 i więcej argumentów, wtedy param_array będzie [argument1, or_more, and_more]

Wadą jest… to niejasne rozwiązanie, naprawdę brzydkie. To jest odpowiedź, że można pominąć argument w środku wywołania funkcji w Rubim :)

Kolejną rzeczą, którą musisz zrobić, jest przepisanie domyślnej wartości zasięgu.

m4risU
źródło
4
To rozwiązanie jest całkowicie błędne. Błędem składni jest użycie parametru wartości domyślnej ( attrs=nil) po splat ( *param_array).
Erik Sandberg
3
-1: Erik ma rację. Powoduje błąd składni w irb 2.0.0p247. Zgodnie z The Ruby Programming Language , w Rubim 1.8 parametr splat musiał być ostatni, z wyjątkiem parametru &parameter, ale w Ruby 1.9 mógł po nim również występować „zwykłe parametry”. W żadnym przypadku parametr z wartością domyślną nie był dozwolony po parametrze z ikoną.
andyg0808
Język programowania Ruby strona 186/187 ikona jest dobra w użyciu z metodami. Musi to być ostatni parametr w metodzie, chyba że użyto &.
rupweb
Więc AndyG ma rację, kolejność musi być następująca: def ldap_get (base_dn, filter, attrs = nil, * param_array)
rupweb
-1

Można to zrobić za pomocą częściowej aplikacji, chociaż używanie nazwanych zmiennych zdecydowanie prowadzi do bardziej czytelnego kodu. John Resig napisał artykuł na blogu w 2008 roku o tym, jak to zrobić w 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);
  };
};

Prawdopodobnie byłoby możliwe zastosowanie tej samej zasady w Rubim (z wyjątkiem dziedziczenia prototypów).

EriF89
źródło