Ruby/Metody: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Szymon wro (dyskusja | edycje)
Szymon wro (dyskusja | edycje)
Nie podano opisu zmian
Linia 74:
 
W [[Ruby/Zmienne klasowe|rozdziale]] dotyczącym zmiennych klasowych zobaczysz zastosowanie słowa kluczowego <tt>self</tt> przy definiowaniu metod należących do całej klasy, czyli metod ''klasowych''.
 
Czasami, analizując różne przykłady kodu w Rubim możemy natknąć się na taką definicję metody (albo wywołanie), w której ostatni parametr poprzedzony jest znakiem <tt>*</tt> lub <tt>&</tt>. Dla początkujących może to wyglądać enigmatycznie, a programistów C/C++/C# mogą dodatkowo mylić skojarzenia ze wskaźnikami i referencjami. Obydwa znaki mają jednak zupełnie inne znaczenie, a ponieważ nie ma nic gorszego od kodu, którego nie rozumiemy, wyjaśnijmy znaczenie obu tych symboli.
 
=== <tt>*</tt> czyli zmienna lista argumentów ===
 
Gwiazdka (<tt>*</tt>) oznacza ''zmienną'' listę argumentów. Jeżeli <tt>*</tt> pojawia się w nagłówku definiowanej metody, poprzedzając ostatni parametr, oznacza to, że począwszy od tego tego argumentu do metody można przekazać dowolną ich ilość. Wszystkie te argumenty są widoczne w metodzie jako tablica.
 
<source lang="ruby">
def metoda(*args)
wynik = ""
args.each {|arg| wynik += "#{arg}, "}
wynik[0...-2] # ucinamy 2 ostanie znaki: ", "
end
 
puts metoda("a", "b", 3) #=> a, b, 3
</source>
 
Gwiazdkę <tt>*</tt> można też stosować w wywołaniu metody, przed ostatnim argumentem - tablicą. Powoduje ona wtedy konwersję z tablicy na poszczególne argumenty:
 
<source lang="ruby">
def inna_metoda(a, b, c)
"#{a}, #{b}, #{c}"
end
 
puts inna_metoda(*["a", "b", 3]) #=> a, b, 3
puts inna_metoda("a", *["b", 3]) #=> a, b, 3
</source>
 
=== & czyli przekazywanie bloku ===
 
[[Ruby/Domknięcia i obiekty procedurowe|Poznaliśmy już]] domknięcia i sposoby przekazywania ich do metody. Domknięcie możemy przekazać, definiując je bezpośrednio za nazwą metody. Natomiast obiekt procedurowy możemy przekazywać jako parametr. Wiemy też, że sterowanie do domknięcia przekazujemy przez <tt>yield</tt>, natomiast procedurę obiektu procedurowego wywołujemy przez metodę <tt>call</tt>. Co jednak, gdy chcielibyśmy użyć bloku przekazanego jako domknięcie tak jakby był obiektem (stosując <tt>call</tt> zamiast <tt>yield</tt>)? Albo gdybyśmy chcieli utworzony już obiekt procedurowy przekazać tak jakby był blokiem?
 
Rozważmy naszą metodę <tt>powtorz</tt> z [[Ruby/Domknięcia i obiekty procedurowe|rozdziału o domknięciach]]:
 
<source lang="ruby">
def powtorz(ilosc)
while ilosc > 0
yield ilosc
ilosc -= 1
end
end
</source>
 
Aby przekazać do tej metody blok, który mamy w postaci np. lambdy, należy użyć symbolu <tt>&</tt> i przekazać nasz blok jako ostatni (niby ''fikcyjny'') argument. Fikcyjny, bo nie jest on jawnie zdefiniowany w nagłówku metody.
 
<source lang="ruby">
l = lambda { |x| print x }
powtorz(3, &l) #=> 321
</source>
 
Efekt jest taki sam jakbyśmy przekazali blok tradycyjnie:
 
<source lang="ruby">
powtorz(3) { |x| print x } #=> 321
</source>
 
Symbolu <tt>&</tt> możemy też używać przed ostatnim parametrem w definicji metody. Dzięki temu, możemy uzyskać niejako odwrotne działanie: odwoływać się do bloku jak do obiektu procedurowego:
 
<source lang="ruby">
def powtorz(ilosc, &blok)
while ilosc > 0
blok.call(ilosc) # to samo co yield ilosc
ilosc -= 1
end
end
 
powtorz(3) { |x| print x } #=> 321
 
l = lambda { |x| print x }
powtorz(3, &l) #=> 321
</source>
 
Jak widzimy, jawne przekazanie obiektu <tt>l</tt> jako bloku również jest poprawne.
 
=== Wiele wartości w instrukcji <tt>return</tt> ===
 
Na koniec powróćmy jeszcze do niuansów stosowania instrukcji <tt>return</tt>. Domyślnie metoda zwraca ostatnie obliczone wyrażenie., choć Możnamożna równieżoczywiście użyćzastosować słowasłowo kluczowegokluczowe <tt>return</tt>, abyby metoda zwróciła konkretną wartość. Jednak w Rubim, w odróżnieniu od C/C++, Javy czy C# instrukcja <tt>return</tt> może zwracać więcej niż jedną wartość. Rozważmy taki przykład:
 
<source lang="ruby">
<pre>
def metoda
return "a", 0, "b"
Linia 90 ⟶ 163:
puts t1.class # => String
puts t2.class # => Fixnum
</presource>
 
Jak widzimy, jeżeli użyjemy jednej zmiennej metoda zwróci nam tablicę, w której będą wszystkie wartości wyrażeń przekazanych do instrukcji <tt>return</tt>. Jeżeli po lewej stronie przypisania wyniku metody umieścimy więcej niż jedną zmienną (<tt>t1</tt>, <tt>t2</tt>, itd.) będą do nich podstawione kolejne wartości zwracane przez <tt>return</tt>. Jeżeli zmiennych po lewej stronie będzie mniej niż wartości zwracanych przez metodę, "nadmiarowe" ''wartości'' zostaną zignorowane (jak ma to miejsce wyżej). Jeżeli natomiast będzie ich więcej, "nadmiarowe" ''zmienne'' dostaną wartości <tt>nil</tt>: