Zaawansowane sterowanie
programem
Działanie każdego większego programu opiera się na licznych skokach i
pętlach.
Wiele problemów rozwiązuje się poprzez wielokrotne operowanie na tych
samych danych.
Pętle
Iteracja to powtarzanie tej samej czynności.
Podstawową metodą iteracji są pętle.
Najstarszy rodzaj pętli – goto
W początkach informatyki, pętla składała się z etykiety, instrukcji
wewnętrznych i skoku. W C++ etykieta to nazwa zakończona dwukropkiem ( :
). Etykieta musi występować w linii jako pierwsza. Instrukcja skoku składa się
ze słowa kluczowego goto i nazwy etykiety docelowej.
Etykieta o nazwie loop
(wyznacza początek pętli).
Przykład:
Dlaczego nie wykorzystuje się instrukcji goto ?
Instrukcja ta pozwala na przejście do dowolnego miejsca w programie, do
przodu lub do tylu. Powoduje to ogromną komplikację programu, taki kod jest
bardzo trudny do czytania i analizy. Określa się go jako „program-
spaghetti”
Aby uniknąć stosowania goto, wprowadzono bardziej zaawansowane i spójne
instrukcje pętli:
for
,
while
do . . . while
.
Wykorzystanie tych instrukcji czyni kod dużo jaśniejszym, instrukcja goto jest
zbędna.
Pętle while
Pętla while pozwala na powtarzanie sekwencji instrukcji tak długo, jak
warunek początkowy jest prawdziwy.
while ( warunek)
{
instrucje;
}
Przykład:
Najpierw sprawdzany
jest warunek, jeśli jest
on
prawdziwy,
to
wykonywane
są
instrukcje
w
pętli.
Kiedy warunek będzie
fałszywy (licznik nie
będzie mniejszy niż 5),
to cała treść pętli
zostanie pominięta.
Złożona instrukcja while
Warunek w instrukcji while może być dowolnie złożonym wyrażeniem C+
+.
Może zawierać wyrażenia utworzone za pomocą operatorów logicznych
&& (and -logiczne "i"), (or -logiczne "lub) i ! (not - negacja).
Przykład:Napisać program realizujący prostą grę. Podajemy dwie liczby,
mniejsza jest zwiększana w każdym kroku o jeden, natomiast druga
zmniejszana o 2. Celem gry jest odgadnięcie, kiedy się spotkają.
Pętla while jest tak długo powtarzana jak prawdziwe są
trzy warunki:
•nMala nie jest większa niż nDuza
•nDuza nie jest ujemna
•nMala nie przekroczyła dopuszczalnego zakresu
MAXMALA
Obliczamy
resztę
z
dzielenia
zmiennej nMala przez 5000. Nie
zmienia to wartości zmiennej. Jeśli
jest ona wielokrotnością 5000, to
warunek jest spełniony i na ekranie
zostaje wypisana kropka.
continue i break
Instrukcja continue oferuje taką możliwość powrócenia do początku pętli
while jeszcze przed zakończeniem aktualnego powtórzenia.
Instrukcja break pozwala na wyjście z pętli wcześniej, niż na to zezwoli
warunek. Przechodzi ona do instrukcji po pętli.
Przykład
Zmniejszanie będzie pominięte, gdy zmienna
nMala będzie wielokrotnością zmiennej nPomin.
Jeśli zmienna nDuza osiągnie wartość
zmiennej nCel to wyświetlany jest
komunikat i gra również się kończy.
Gra kończy się, gdy zmienna nMala
przyjmie większą wartość niż zmienna
nDuza.
Zadaniem użytkownika jest podać taką wartość
zmiennej nCel (odpowiednią dla zmiennej nDuza),
aby gra się zakończyła zanim zmienna nMala stanie
się większa niż nDuza.
Instrukcje continue i break powinny być stosowane bardzo
rozważnie. Podobnie jak goto bardzo gmatwają one kod. Program
niespodziewanie przeskakuje w zupełnie inne miejsca. Nawet mała
pętla while może się stać całkowicie nieczytelna.
Przykład
Ponieważ
wartość
1
oznacza
prawdę, to zakończyć pętle może
jedynie instrukcja break.
Ten program działa, ale trudno nazwać go eleganckim. Jest to dobry przykład
użycia niewłaściwego narzędzia. To samo można osiągnąć poprzez
umieszczenie warunku zakończenia pętli w miejscu dla niego wyznaczonym -
w instrukcji while.
Nieskończone pętle, takie jak while (1) mogą spowodować zawieszenie
komputera w przypadku, w którym warunek zakończenia nigdy nie
zostanie spełniony.
Pętle
do . . . while
Możliwe jest, że pętla while nie wykona się ani razu. Warunek w while
sprawdzany jest przed wykonaniem instrukcji w pętli, zatem jeśli jest on od
razu fałszywy, to pętla zostanie pominięta.
Przykład
Za
pierwszym
razem,
kiedy
użytkownik podał wartość 3, pętla
wykonała się trzykrotnie.
Za drugim razem, gdy użytkownik
podał wartość 0, pętla nie wykona się
ani razu.
Co zrobić, jeśli chcemy, aby program wypisał "Cześć!" przynajmniej
raz?
Pętla while nie daje takiej możliwości, ponieważ warunek wykonania
sprawdzany jest jeszcze przed wejściem do pętli. Pewnym rozwiązaniem jest
nadanie odpowiedniej wartości zmiennej licznik z wykorzystaniem instrukcji if
tuż przed pętlą while:
if (nLicznik < 1) //wymuś wartość minimalną
nLicznik=1;
Pętla do. . . while gwarantuje, że instrukcje w pętli zostaną wykonane
przynajmniej raz. Warunek sprawdzany jest nie przed wykonaniem lecz po.
Przykład
Podobnie jak w pętli while także w pętli do. . . while można wykorzystywać
instrukcje break i continue.
Oba rodzaje pętli różnią się jedynie miejscem sprawdzania warunku, w
pętli while sprawdzany jest on przed wykonaniem instrukcji pętli,
natomiast w pętli do. . . while po wykonaniu instrukcji pętli.
do
{
instrukcje;
} while ( wyrażenie );
Pętla for
Kiedy wykorzystuje się pętlę while, to zazwyczaj ustala się jakąś wartość
początkową, a w każdym kroku pętli sprawdza się warunek związany z tą
wartością i w jakiś sposób zmienia się ją wewnątrz pętli.
Przykład
Pętla for łączy w sobie trzy elementy działania pętli - inicjalizację,
sprawdzenie warunku i zmianę wartości.
Instrukcja for składa się ze słowa kluczowego for i pary nawiasów.
Wewnątrz nawiasów umieszcza się trzy instrukcje oddzielone średnikami.
Uwaga:
pierwsze i trzecie wyrażenie może być dowolną poprawną
instrukcją C++. Natomiast drugie wyrażenie musi być wyrażeniem
zwracającym jakąś wartość.
for ( wyrażenie1; wyrażenie2; wyrażenie3 )
{
instrukcje ;
}
Pierwsze wyrażenie to inicjalizacja.
Można tu umieścić każdą poprawną
instrukcję C++ jednak zazwyczaj
umieszcza się inicjalizację zmiennej
kontrolującej wykonywanie pętli.
Drugie wyrażenie to sprawdzenie
warunku, tutaj może się znaleźć
dowolne, poprawne wyrażanie C++.
Spełnia ono taką samą rolę jak
warunek w pętli while.
Trzecie wyrażenie to jakaś akcja. Zazwyczaj umieszcza się tutaj
inkrementację albo dekrementację zmiennej kontrolującej pętlę, jednak
można tu wstawić dowolną poprawną instrukcję C++.
Pętla for daje bardzo dużo możliwości i jest bardzo elastyczna. Trzy
niezależne operacje (inicjalizacja, sprawdzenie warunku, akcja) mogą
występować w bardzo wielu wariantach.
Pętla for działa według następującego schematu:
1. Wykonaj instrukcje inicjalizacji.
2. Wylicz wartość wyrażenia w warunku.
3. Jeśli warunek jest spełniony to wykonaj akcję i instrukcje
pętli.
W każdym kroku pętli powtarzane są punkty 2 i 3.
Przykład
Bardzo często w jednej pętli inicjalizujemy wiele zmiennych, sprawdzamy
złożone warunki i wykonujemy wiele instrukcji. Instrukcje inicjalizacji i akcji
można w C++ zamienić na instrukcję wielokrotną oddzielając je przecinkami.
Przykład
Każde wyrażenie w pętli for może być puste (w szczególnym przypadku
wszystkie jednocześnie). Aby to osiągnąć należy wstawić średniki tam, gdzie
powinny być wyrażenia.
Jeżeli za pomocą pętli for chcecie uzyskać pętlę równoważną pętli while, to
trzeba pominąć pierwsze i trzecie wyrażenie.
while ( nLicznik < 10)
Przykład
Ten szczególny program wygląda nieco absurdalnie. Jednak czasami, takie
instrukcje jak while (1) albo for ( ; ; ) znajdują swoje zastosowanie.
Inicjalizacja wykonana została
przed rozpoczęciem pętli for.
Warunek stopu pętli to
oddzielna instrukcja if.
W instrukcji for można wykonać tak wiele operacji, że często nie potrzeba
żadnej treści pętli. W takim przypadku należy pamiętać o umieszczeniu znaku
średnika (oznaczającego instrukcję pusta) za instrukcją for (można go
umieścić w tej samej linii co instrukcja for, ale łatwo go wtedy przegapić).
Przykład
Zwróćmy uwagę, że nie
jest
to
dobrze
zaprojektowana instrukcja
for. Ostatnia instrukcja -
akcja
-
wykonuje
stanowczo
za
wiele
czynności. Lepiej by było
tak:
for ( int i = 0; i < 10; i++ )
{
cout << ” i: ” << i << endl;
}
W obydwu przypadkach osiągamy ten sam rezultat, jednak drugie
rozwiązanie jest bardziej eleganckie i czytelne.
Zagnieżdżone pętle for
Pętle można dowolnie zagnieżdżać poprzez umieszczenie pętli w treści innej
pętli. Taka wewnętrzna pętla będzie wykonywana przy każdej iteracji pętli, w
której jest umieszczona (czyli pętli zewnętrznej).
Przykład
Ważne jest to, że w przypadku pętli
zagnieżdżonych, pętla wewnętrzna jest
wykonywana w każdej iteracji pętli
zewnętrznej.
Instrukcja switch
Kombinacje instrukcji if oraz if. . . else, w przypadku głębokiego
zagnieżdżenia, mogą powodować spore zamieszanie.
C++ oferuje tutaj wygodne, alternatywne rozwiązanie. W przeciwieństwie do
instrukcji if, która potrafi rozpatrzyć tylko jedną wartość jednocześnie)
instrukcja switch pozwala na wyszczególnienie dowolnej liczby wartości.
Ogólny schemat instrukcji switch:
switch ( wyrażenie )
{
case wartość1 : instrukcje;
break;
case wartość2 : instrukcje;
break;
...
case wartość#n: instrukcje;
break;
default:
instrukcje;
}
wyrażenie to dowolne poprawne wyrażenie
C++
instrukcje to dowolne
poprawne instrukcje C++
(również bloki instrukcji)
Instrukcja switch oblicza wartość
podanego wyrażenia i porównuje z
wartościami podanymi przy case.
Zauważmy, że sprawdzana jest
tylko relacja równości, nie można
tu
możliwości
wykorzystania
innych relacji.
Jeśli któraś z wartości przy case
jest równa wartości wyrażenia to
program przechodzi do podanych
dalej instrukcji i wykonuje wszystko
do końca bloku switch tak długo,
aż napotka instrukcję break.
Jeśli żadna z wartości nie jest równa wartości wyrażenia to wykonywane są
instrukcje przy słowie kluczowym default. Jeśli nie określi się instrukcji
default i program nie odnajdzie pasującej wartości, to instrukcja switch nie
zrobi nic, zostanie pominięta.
Bardzo ważne jest, że jeśli nie ma instrukcji break na końcu każdego case,
to program wykona wszystkie instrukcje od pasującego case aż do końca
switch (jeśli gdzieś niżej napotka break, to oczywiście przerwie
wykonywanie). Jeżeli decydujecie się na takie rozwiązanie, to pamiętajcie o
umieszczeniu odpowiedniego komentarza informującego o tym, że brak
break jest zamierzony.
Przykład
Jest wiele różnych metod tworzenia pętli w C++:
while sprawdza warunek, jeśli jest on spełniony, to zostaje wykonana
treść pętli.
do ... while najpierw wykonuje instrukcje pętli, a dopiero potem sprawdza
warunek. for pozwala na jednoczesną inicjalizację wartości i sprawdzenie
warunku. Jeśli warunek jest spełniony, to wykonywana jest ostatnia instrukcja
w for (akcja), a następnie treść pętli. Przed każdą iteracją ponownie
sprawdzany jest warunek.
Instrukcja goto jest raczej pomijana, ponieważ powoduje bezwarunkowy
skok do zupełnie innego miejsca w programie, co czyni go nieczytelnym
(program spaghetti).
Instrukcja continue powoduje przejście do początku pętli while, do...
while oraz for. Instrukcja break natychmiastowo przerywa wykonywanie
podanych pętli oraz switch.