Ruby/Zmienne lokalne: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Szymon wro (dyskusja | edycje)
Nie podano opisu zmian
Szymon wro (dyskusja | edycje)
Nie podano opisu zmian
Linia 4:
 
<pre>
rubyirb(main):001:0> $a
=> nil
rubyirb(main):002:0> @a
=> nil
irb(main):003:0> a
ruby> a
ERR: (eval):1NameError: undefined local variable or method `a' for main(:Object)
from (irb):3
</pre>
 
Linia 24 ⟶ 25:
* cały skrypt (chyba że zastosowano jedno z powyższych)
 
WUżyty w następnym przykładzie <tt>defined?</tt> jest operatorem, który sprawdza czy identyfikator został zdefiniowany. Zwraca on opis identyfikatora, jeśli jest on zdefiniowany lub, w przeciwnym razie, <tt>nil</tt>. Jak widzisz, zasięg zmiennej <tt>b</tt> jest ograniczony lokalnie do pętli. Gdy pętla zostaje przerwana zmienna <tt>b</tt> jest niezdefiniowana.
 
<source lang="ruby">
<pre>
a = 44
ruby> a = 44; puts a; defined?(a)
puts defined?(a) #=> local-variable
44
"local-variable"
ruby> loop {b=45; puts b; break}; defined?(b)
45
nil
</pre>
 
loop do
Obiekty procedurowe, które żyją w pewnym zakresie widoczności współdzielą tylko zmienne lokalne, które również należą do tego zakresu. Tutaj, zmienna lokalna <tt>a</tt> jest współdzielona przez <tt>main</tt> oraz obiekty procedurowe <tt>p1</tt> i <tt>p2</tt>:
b=45
break
end
puts defined?(b) #=> nil
</source>
 
Obiekty procedurowe, które żyją w pewnym zakresie widoczności współdzielą tylko te zmienne lokalne, które również należą do tego zakresu. Tutaj, zmienna lokalna <tt>a</tt> jest współdzielona przez <tt>main</tt> oraz obiekty procedurowe <tt>p1</tt> i <tt>p2</tt>:
<pre>
ruby> a = nil
nil
ruby> p1 = proc {|n| a=n}
#<Proc:0x8deb0>
ruby> p2 = proc {a}
#<Proc:0x8dce8>
ruby> p1.call(5)
5
ruby> a
5
ruby> p2.call
5
</pre>
 
<source lang="ruby">
Zauważ, że nie można pominąć <tt>a = nil</tt> na początku. To przypisanie zapewnia, że zasięg zmiennej <tt>a</tt> obejmie <tt>p1</tt> i <tt>p2</tt>. Inaczej <tt>p1</tt> i <tt>p2</tt> utworzyłyby swoje własne zmienne lokalne <tt>a</tt>, i rezultatem wywołania <tt>p2</tt> byłby błąd "undefined local variable or method" (niezdefiniowana zmienna lokalna lub metoda). Moglibyśmy użyć <tt>a = 0</tt>, ale użycie <tt>nil</tt> jest pewną uprzejmością wobec przyszłych czytelników naszego kodu. Pokazuje to naprawdę jasno, że tylko ustanawiamy zakres, ponieważ wartość przypisywana do zmiennej nie jest zawiera żadnego znaczenia (<tt>nil</tt>).
a = nil
l1 = lambda {|n| a=n}
l2 = lambda {a}
l1.call(5)
puts a #=> 5
puts l2.call #=> 5
</source>
 
Zauważ, że nie można pominąć <tt>a = nil</tt> na początku. To przypisanie zapewnia, że zasięg zmiennej <tt>a</tt> obejmie <tt>l1</tt> i <tt>l2</tt>. Inaczej <tt>l1</tt> i <tt>l2</tt> utworzyłyby swoje własne zmienne lokalne <tt>a</tt>, i rezultatem wywołania <tt>l2</tt> byłby błąd "undefined local variable or method" (niezdefiniowana zmienna lokalna lub metoda). Moglibyśmy użyć <tt>a = 0</tt>, ale użycie <tt>nil</tt> jest pewną uprzejmością wobec przyszłych czytelników naszego kodu. Pokazuje naprawdę jasno, że tylko ustanawiamy zakres, ponieważ wartość przypisana do zmiennej nie niesie żadnego specjalnego znaczenia (<tt>nil</tt>).
Potężna zaleta obiektów procedurowych wypływa z ich zdolności do bycia przekazywanymi jako argumenty: współdzielone zmienne lokalne pozostają poprawne nawet wtedy, gdy przekazane są poza pierwotny zakres.
 
W rozdziale o [[Roby/Domknięcia i obiekty procedurowe|domknięciach i obiektach procedurowych]] wspomnieliśmy, że domknięcia i obiekty procedurowe zachowują kontekst używanych zmiennych lokalnych. Jest to bardzo potężna zaleta: współdzielone zmienne lokalne pozostają poprawne nawet wtedy, gdy przekazane są poza pierwotny zakres.
<pre>
ruby> def pudelko
| zawartosc = nil
| wez = proc {zawartosc}
| wloz = proc {|n| zawartosc = n}
| return wez, wloz
| end
nil
ruby> odczyt, zapis = pudelko
[#<Proc:0x40170fc0>, #<Proc:0x40170fac>]
ruby> odczyt.call
nil
ruby> zapis.call(2)
2
ruby> odczyt.call
2
</pre>
 
<source lang="ruby">
Ruby jest szczególnie sprytny jeśli chodzi o zakres. Ewidentnie w naszym przykładzie widać, że zmienna <tt>zawartosc</tt> jest współdzielona pomiędzy <tt>odczyt</tt> i <tt>zapis</tt>. Możemy również wytworzyć wiele par <tt>odczyt-zapis</tt> używając metody <tt>pudelko</tt> zdefiniowanej powyżej. Każda para współdzieli zmienną <tt>zawartosc</tt>, ale pary nie kolidują ze sobą nawzajem. Dopóki istnieją obiekty procedurowe, zachowane są ich konteksty wywołania wraz z odpowiadającymi im zmiennymi lokalnymi.
def pudelko
zawartosc = nil
wez = lambda { zawartosc }
wloz = lambda { |n| zawartosc = n }
return wez, wloz
end
 
odczyt, zapis = pudelko
<pre>
 
ruby> odczyt_1, zapis_1 = pudelko
puts odczyt.call #=> nil
[#<Proc:0x40172820>, #<Proc:0x4017280c>]
zapis.call(2)
ruby> odczyt_2, zapis_2 = pudelko
puts odczyt.call #=> 2
[#<Proc:0x40172668>, #<Proc:0x40172654>]
</source>
ruby> zapis_1.call(99)
 
99
Ruby jest szczególnie sprytny jeśli chodzi o zakres. W naszym przykładzie ewidentnie widać, że zmienna <tt>zawartosc</tt> jest współdzielona pomiędzy <tt>odczyt</tt> i <tt>zapis</tt>. Możemy również wytworzyć wiele par <tt>odczyt-zapis</tt> używając metody <tt>pudelko</tt> zdefiniowanej powyżej. Każda para współdzieli zmienną <tt>zawartosc</tt>, ale pary nie kolidują ze sobą nawzajem. Dopóki istnieją obiekty procedurowe, zachowane są ich konteksty wywołania wraz z odpowiadającymi im zmiennymi lokalnymi!
ruby> odczyt_1.call
 
99
<source lang="ruby">
ruby> odczyt_2.call # w tym pudelku jeszcze nic nie ma
odczyt_1, zapis_1 = pudelko
nil
odczyt_2, zapis_2 = pudelko
</pre>
 
zapis_1.call(99)
puts odczyt_1.call #=> 99
 
# w tym pudelku jeszcze nic nie ma
puts odczyt_2.call #=> nil
</source>
 
TenPowyższy rodzaj programowaniakod można by wręcz uważać za lekko perwersyjny zorientowany obiektowo [[w:Framework|framework]]. Metoda <tt>pudelko</tt> odgrywa rolę klasy podczas gdy <tt>wez</tt> i <tt>wloz</tt> służą jako metody, natomiast <tt>zawartosc</tt> jest samotną zmienną instancji. Oczywiście stosowanie właściwego szkieletu klas Rubiego prowadzi do znacznie bardziej czytelnego kodu ;).
<noinclude>
{{ProstaNawigacja|spis=Ruby|poprzart=Ruby/Zmienne instancji|poprz=Zmienne instancji|nastart=Ruby/Stałe klasowe|nast=Stałe klasowe}}