Ruby/Struktury sterujące

Struktury sterujące edytuj

Ten rozdział odkrywa nieco więcej na temat struktur sterujących Rubiego.

case edytuj

Instrukcji case używamy do sprawdzenia sekwencji warunków. Na pierwszy rzut oka jest ona podobna do instrukcji switch z języka C lub Java ale, jak za chwilę zobaczymy, znacznie potężniejsza.

def okresl(i)
  case i
  when 1, 2..5
    puts "1..5"
  when 6..10
    puts "6..10"
  end
end

okresl(8) #=> 6..10

2..5 jest wyrażeniem oznaczającym przedział zamknięty od 2 do 5. Następujące 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. W odniesieniu do obiektowej natury Rubiego, === jest interpretowany odpowiednio dla obiektu który pojawia się jako warunek w instrukcji when. Na przykład, następujący kod sprawdza równość łańcuchów znakowych w pierwszym wyrażeniu when oraz zgodność wyrażeń regularnych w drugim when.

def okresl(s)
  case s
  when 'aaa', 'bbb'
    puts "aaa lub bbb"
  when /def/
    puts "zawiera def"
  end
end

okresl("abcdef") #=> zawiera /def/

while edytuj

Ruby dostarcza wygodnych sposobów do tworzenia pętli, chociaż jak odkryjesz w następnym rozdziale, wiedza o tym jak używać iteratorów często zaoszczędzi ci bezpośredniego pisania własnych pętli.

while jest powtarzanym if. Używaliśmy tej instrukcji w naszej słownej zgadywance i w programach sprawdzających wyrażenia regularne (zobacz poprzedni rozdział). Tutaj instrukcja ta przyjmuje formę while warunek ... end otaczającą blok kodu który będzie powtarzany dopóty, dopóki warunek jest prawdziwy. Ale while i if mogą być łatwo zaaplikowane również do pojedynczych wyrażeń:

irb(main):001:0> i = 0
=> 0
irb(main):002:0> puts "To jest zero." if i == 0
To jest zero.
=> nil
irb(main):003:0> puts "To jest liczba ujemna" if i < 0
=> nil
irb(main):004:0> puts i += 1 while i < 3
1
2
3
=> nil

Czasami będziesz chciał zanegować sprawdzany warunek. unless jest zanegowanym if, natomiast until zanegowanym while. Jeśli chcesz, poeksperymentuj z tymi instrukcjami.

Są cztery sposoby do przerywania wykonywania pętli z jej wnętrza. Pierwszy, to zastosowanie instrukcji break. Powoduje, tak jak w C, zupełną ucieczkę z pętli. Drugi, to zastosowanie instrukcji next. Powoduje ona przeskoczenie na początek kolejnej iteracji (podobnie jak znane z C continue). Trzeci, to użycie specyficznej dla Rubiego instrukcji redo, która powoduje ponowne wykonanie bieżącej iteracji. Następujący kod w języku C ilustruje znaczenie instrukcji break, next i redo:

while (warunek) {
etykieta_redo:
   goto etykieta_next;        /* w Rubim: "next" */
   goto etykieta_break;       /* w Rubim: "break" */
   goto etykieta_redo;        /* w Rubim: "redo" */
   ...
   ...
etykieta_next:
}
etykieta_break:
...

Czwarty sposób by wyjść z pętli będąc w jej wnętrzu to użycie instrukcji return. Jej wywołanie spowoduje wyjście nie tylko z pętli ale również z metody, która tę pętlę zawiera. Jeżeli podany został argument, będzie on zwrócony jako rezultat wywołania metody. W przeciwnym wypadku zwracane jest nil.

for edytuj

Programiści C mogą się zastanawiać jak zrobić pętlę "for". Petla for Rubiego może służyć w ten sam sposób, choć jest nieco bardziej elastyczna. Pętla poniżej iteruje każdy element w kolekcji (tablicy, tablicy asocjacyjnej, sekwencji numerycznej, itd.), ale nie zmusza programisty do myślenia o indeksach:

for element in kolekcja
  # ... "element" wskazuje na element w kolekcji
end

Kolekcją może być przedział wartości (to właśnie większość ludzi ma na myśli, gdy mówi o pętli for):

for num in (4..6)
  print num
end
#=> 456

W tym przykładzie przeiterujemy kilka elementów tablicy:

for elem in [100, -9.6, "zalewa"]
  puts "#{elem}\t(#{elem.class})"
end
#=> 100     (Fixnum)
#   -9.6    (Float)
#   zalewa  (String)

Ale tak naprawdę for jest po prostu innym sposobem zapisania instrukcji each, która jest naszym pierwszym przykładem iteratora. Poniższe formy są równoważne:

Jeżeli przywykłeś do C lub Javy, możesz preferować tą.

for element in kolekcja
  ...
end

Natomiast programista Smalltalka może preferować taką.

kolekcja.each {|element|
  ...
}

Iteratory często mogą być używane zamiast konwencjonalnych pętli. Jak już nabierzesz wprawy w ich użyciu, stają się zazwyczaj łatwiejsze w użyciu od pętli. Ale zanim dowiemy się więcej o iteratorach, poznajmy jedną najciekawszych konstrukcji języka Ruby: domknięcia.