Ruby/Domknięcia i obiekty procedurowe: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Nie podano opisu zmian
 
Linia 5:
Ruby jest językiem korzystającym w dużym stopniu z domknięć. Domknięcie jest blokiem kodu przekazywanym do metody. Samo w sobie nie jest obiektem. Domknięcie zawierające niewiele instrukcji, które można zapisać w jednej linii zapisujemy pomiędzy nawiasami klamrowymi (<tt>{}</tt>), zaraz za wywołaniem metody:
 
<sourcesyntaxhighlight lang="ruby">
3.times { print "Bla" } #=> BlaBlaBla
</syntaxhighlight>
</source>
 
Domknięcia dłuższe zapisujemy w bloku <tt>do ... end</tt>
 
<sourcesyntaxhighlight lang="ruby">
i = 0
3.times do
Linia 18:
end
#=> 024
</syntaxhighlight>
</source>
 
Obsługa bloku przekazanego do funkcji odbywa się poprzez słowo kluczowe <tt>yield</tt>, które przekazuje sterowanie do bloku. Spójrzmy na przykład metody <tt>powtorz</tt>.
 
<sourcesyntaxhighlight lang="ruby">
def powtorz(ilosc)
while ilosc > 0
Linia 31:
powtorz(3) { print "Bla" } #=> BlaBlaBla
</syntaxhighlight>
</source>
 
Po zakończeniu wykonywania przekazanego bloku sterowanie wraca z powrotem do metody. Dzięki słowu kluczowemu <tt>yield</tt> możemy również przekazywać do bloku obiekty:
 
<sourcesyntaxhighlight lang="ruby">
def powtorz(ilosc)
while ilosc > 0
Linia 42:
end
end
</syntaxhighlight>
</source>
 
Aby użyć wartości przekazanej do bloku stosujemy identyfikator ujęty w znaki <tt>|</tt>:
 
<sourcesyntaxhighlight lang="ruby">
powtorz(3) { |n| print "#{n}.Bla " } #=> 3.Bla 2.Bla 1.Bla
</syntaxhighlight>
</source>
 
Co jednak, gdy używamy <tt>yield</tt>, a do metody nie przekazaliśmy żadnego bloku? Aby uchronić się przed wystąpieniem wyjątku używamy metody <tt>block_given?</tt>, która zwraca <tt>true</tt>, gdy blok został przekazany.
 
<sourcesyntaxhighlight lang="ruby">
def powtorz(ilosc)
if block_given?
Linia 66:
powtorz(3) # nie przekazujemy bloku
#=> Brak bloku
</syntaxhighlight>
</source>
 
=== Obiekty procedurowe ===
Linia 72:
Bloki można zamienić w łatwy sposób na obiekty (są to obiekty klasy <tt>Proc</tt>. O tym, czym dokładnie są obiekty i klasy dowiesz się w [[Ruby/Klasy|rozdziale o klasach]].) Można użyć w tym celu słów kluczowych <tt>lambda</tt> lub <tt>proc</tt>, z czego '''zalecane''' jest to pierwsze. Poniższy kod utworzy dwa obiekty procedurowe:
 
<sourcesyntaxhighlight lang="ruby">
hej = lambda { print "Hej" }
 
Linia 78:
puts "Witaj!"
end
</syntaxhighlight>
</source>
 
Aby wykonać dany blok zawarty w obiekcie procedurowym (wywołać go) należy użyć metody <tt>call</tt>:
 
<sourcesyntaxhighlight lang="ruby">
hej.call #=> Hej
witaj.call #=> Witaj!
</syntaxhighlight>
</source>
 
W wywołaniu <tt>call</tt> możemy również przekazać parametry do bloku:
 
<sourcesyntaxhighlight lang="ruby">
drukuj = lambda { |tekst| print tekst }
drukuj.call("Hop hop!") #=> Hop hop!
</syntaxhighlight>
</source>
 
Obiekty procedurowe mogą być, jak każde inne obiekty, przekazywane jako parametry. Możemy zdefiniować alternatywną metodę <tt>powtorz</tt> która bedzie wykorzystywać lambdę przekazaną jako parametr. Rozważmy poniższy przykład:
 
<sourcesyntaxhighlight lang="ruby">
def powtorz(ile, co)
while ile > 0
Linia 110:
powtorz(3, l) #=> 321
powtorz(3, lambda { print "bla" }) #=> blablabla
</syntaxhighlight>
</source>
 
Jak widzimy w ostatniej linii, obiekty lambda mogą być anonimowe (nie nadajemy im żadnej nazwy). O obiektach anonimowych dowiemy się wkrótce więcej. Natomiast w [[Ruby/Zmienne lokalne|rozdziale o zmiennych lokalnych]] zobaczymy, że obiekty procedurowe i domknięcia zachowują kontekst (stan zmiennych lokalnych) w jakim zostały wywołane.
Linia 120:
Surowe obiekty <tt>Proc</tt> (ang. ''raw procs''), czyli utworzone poprzez <tt>Proc.new</tt>, posiadają jedną niedogodność: użycie instrukcji <tt>return</tt> powoduje nie tyle wyjście z domknięcia obiektu procedurowego, co wyjście z całego bloku, w którym domknięcie było wywołane. Może to powodować niespodziewane wyniki działania naszych programów, dlatego zaleca się używanie lambd, a nie surowych obiektów <tt>Proc</tt>.
 
<sourcesyntaxhighlight lang="ruby">
def proc1
p = Proc.new { return -1 }
Linia 131:
puts "Blok zwraca #{p.call}"
end
</syntaxhighlight>
</source>
 
Wywołany <tt>proc1</tt> zwraca jedynie wartość, nie wypisze żadnego tekstu. Odmiennie działa <tt>proc2</tt> - tutaj <tt>return</tt> powoduje, że sama lambda zwraca wartość, do której można się odwołać w dalszej części bloku, w którym utworzono lambdę.