Struktury kontrolne

Rozdział ten odkrywa więcej struktur sterujących Rubiego.

case

Używamy instrukcji case do przetestowania sekwencji warunków. To pozornie podobna instrukcja do switch'a z C i Javy, ale jest znacznie bardziej potężny, tak jak to powinniśmy zobaczyć poniżej.

ruby> i=8
ruby> case i
    | when 1, 2..5
    |   puts "1..5"
    | when 6..10
    |   puts "6..10"
    | end
6..10
   nil

2..5 jest wyrażeniem oznaczającym przedział od 2 do 5 włącznie. Poniższe wyrażenie sprawdza czy wartość i należy do tego przedziału:

(2..5) === i

case wewnętrznie używa operatora relacji === by sprawdzić kilkanaście warunków za jednym razem. Zgodnie z obiektową naturą Rubiego, === jest interpretowany odpowiednio dla obiektu, który ukazał się w warunku. Na przykład, następujący kod sprawdza w pierwszym przypadku równość łańcuchów znakowych, kiedy w drugim przypadku sprawdza dopasowanie do wyrażeń regularnych.

ruby> case 'abcdef'
    | when 'aaa', 'bbb'
    |   puts "aaa lub bbb"
    | when /def/
    |   puts "includes /def/"
    | end
includes /def/
   nil

while

Ruby dostarcza wygodne sposoby do tworzenia pętli, chociaż można znaleźć w następnym rozdziale, że uczenie się, jak używać iteratorów będzie bardzo często zbędne do pisania wyraźnych pętli.

while jest powtarzanym if'em. Użyliśmy go w naszej zgadywance i w programach z wyrażeniami regularnymi (patrz poprzedni rozdział); tam, to przyjmuje formę while warunek ... end otaczającą blok powtarzanego kodu dopóki warunek jest prawdą. Ale while i if, mogą z łatwością być stosowane jako indywidualne instrukcje:

ruby> i = 0
   0
ruby> puts "To jest zero." if i==0
To jest zero.
   nil
ruby> puts "To liczba ujemna." if i<0
   nil
ruby> puts i+=1 while i<3
1
2
3
   nil

Czasami będziesz chciał zanegować sprawdzany warunek. unless jest zanegowanym if'em i until jest zanegowanym while'm. Pozostawimy ją dla Ciebie do eksperymentowania na niej.

Istnieją cztery możliwości przerwania przebiegu pętli od wewnątrz. Pierwsza, break, znaczy to samo co w C, aby uciec z pętli całkowicie. Druga, next przeskakująca do początku następnej iteracji tej pętli (zgodna do continue z języka C). Trzecia, rubiego redo, która oznacza ponowne wykonanie bieżącej iteracji. Następujący kod w języku C ilustruje znaczenia instrukcji break, next i redo:

while(warunek) {
label_redo:
   goto label_next;        /* ruby's "next" */
   goto label_break;       /* ruby's "break" */
   goto label_redo;        /* ruby's "redo" */
   ...
   ...
label_next:
}
label_break:
...

Czwartym sposobem by wyjść z wnętrza pętli jest return. Dokonanie oceny return'a powoduje wyjście nie tylko z pętli lecz także z metody, która zawiera pętlę. Jeżeli podany został argument, będzie on zwrócony z wywołania metody, w przeciwnym wypadku zwrócony będzie nil.

for

Programiści C będą się teraz zastanawiać, jak zrobić pętlę "for". For w Ruby może służyć temu samemu celowi, ale dodaje pewną elastyczność. Poniższa pętla działa raz dla każdego elementu zbioru (array, hash, sekwencji numerycznej, itd.), ale sprawia że programista nie myśli o indeksach:

for elt in collection
  # ... tutaj, elt odnosi się do elementu z kolekcji
end

Zbiór może być zakresem wartości (tym o czym większość ludzi myśli, kiedy mówimy o pętli for):

ruby> for num in (4..6)
    |    puts num
    | end
4
5
6
   4..6

W tym przykładzie przechodzimy przez niektóre elementy tablicy:

ruby> for elt in [100,-9.6,"pickle"]
    |    puts "#{elt}\t(#{elt.class})"
    | end
100    (Fixnum)
-9.6   (Float)
pickle (String)
   [100, -9.6, "pickle"]

Jesteśmy coraz dalej. for wraz z each jest naprawdę innym sposobem wypisywania każdego elementu, który tak się składa, jest naszym pierwszym przykładem iteratora. Poniższe dwie formy są równoważne:

#  Jeśli używałeś C lub Javy, możesz woleć ten.
for element in collection
  ...
end

#  Programiści Smalltalk'a mogą woleć ten.
collection.each {|element|
  ...
}

Iteratory często mogą być zastępowane zwykłymi pętlami i po przyzwyczajeniu się do nich, są na ogół łatwiejsze do zaaplikowania. Więc przejdźmy dalej i nauczmy się o nich więcej.