BAZA WIEDZY
KURSY
Bazy danych w PHP
Kurs AdvancedAJAX
Kurs ASP
Kurs ASP.NET
Kurs C++
Kurs CSS
Kurs HTML
Kurs HTML drugi
Kurs JavaScript
Kurs MySQL
Kurs PHP
Kurs RSS
Kurs XHTML
Obiekty DOM
MANUALE
CSS1 - W3C
DOM - w budowie
PHP 2005
PHP 2006
Wyrażenia regularne
SHOUTBOX
STAT
Online: 4 | UU: 228

Sterowanie warunkowe

Dobrze napisany program powinien być przygotowany na każdą ewentualność i nietypową sytuację, jaka może się przydarzyć w czasie jego działania. W niektórych przypadkach nawet proste czynności mogą potencjalnie kończyć się niepowodzeniem, zaś porządna aplikacja musi radzić sobie z takimi drobnymi (lub całkiem sporymi) kryzysami. Oczywiście program nie uczyni nic, czego nie przewidziałby jego twórca. Dlatego też ważnym zadaniem programisty jest opracowanie kodu reagującego odpowiednio na nietypowe sytuacje, w rodzaju błędnych danych wprowadzonych przez użytkownika lub braku pliku potrzebnego aplikacji do działania.

Możliwe są też przypadki, w których dla kilku całkowicie poprawnych sytuacji, danych itp. trzeba wykonać zupełnie inne operacje. Ważne jest wtedy rozróżnienie tych wszystkich wariantów i skierowanie działania programu na właściwe tory, gdy ma miejsce któryś z nich.

Do wszystkich tych zadań stworzono w C++ (i w każdym języku programowania) zestaw odpowiednich narzędzi, zwanych instrukcjami warunkowymi . Ich przeznaczeniem jest właśnie dokonywanie różnorakich wyborów, zależnych od ustalonych warunków .

Jak widać, przydatność tych konstrukcji jest nadspodziewanie duża, żal byłoby więc ominąć je bez wnikania w ich szczegóły, prawda? :) Niezwłocznie zatem zajmiemy się nimi, poznając ich składnię i sposób funkcjonowania.

Instrukcja warunkowa if

Instrukcja if ('jeżeli') pozwala wykonać jakiś kod tylko wtedy, gdy spełniony jest określony warunek. Jej działanie sprowadza się więc do sprawdzenia tegoż warunku i, jeśli zostanie stwierdzona jego prawdziwość, wykonania wskazanego bloku kodu.

Tę prostą ideę może ilustrować choćby taki przykład:

// SimpleIf - prosty przykład instrukcji if
void main()
{
int nLiczba;
std::cout << "Wprowadz liczbe wieksza od 10: " ;
std::cin >> nLiczba;
if (nLiczba > 10 )
{
std::cout << "Dziekuje." << std::endl;
std::cout << "Wcisnij dowolny klawisz, by zakonczyc." ;
getch();
}
}

Uruchom ten program dwa razy - najpierw podaj liczbę mniejszą od 10, zaś za drugim razem spełnij życzenie aplikacji. Zobaczysz, że w pierwszym przypadku zostaniesz potraktowany raczej mało przyjemnie, gdyż program bez słowa zakończy się. W drugim natomiast otrzymasz stosowne podziękowanie za swoją uprzejmość ;)

" Winna" jest temu, jakżeby inaczej, właśnie instrukcja if . W linijce:

if (nLiczba > 10 )

wykonywane jest bowiem sprawdzenie, czy podana przez ciebie liczba jest rzeczywiście większa od 10. Wyrażenie nLiczba > 10 jest tu więc warunkiem instrukcji if .

W przypadku, gdy okaże się on prawdziwy, wykonywane są trzy instrukcje zawarte w nawiasach klamrowych. Jak pamiętamy, sekwencję taką nazywamy blokiem kodu .

Jeżeli zaś warunek jest nieprawdziwy (a liczba mniejsza lub równa 10), program omija ten blok i wykonuje następną instrukcję występującą za nim. Ponieważ jednak u nas po bloku if nie ma żadnych instrukcji, aplikacja zwyczajnie kończy się, gdyż nie ma nic konkretnego do roboty :)

Po takim sugestywnym przykładzie nie od rzeczy będzie przedstawienie składni instrukcji warunkowej if w jej prostym wariancie:

if ( warunek )
{
instrukcje
}

Stopień jej komplikacji z pewnością sytuuje się poniżej przeciętnej ;) Nic w tym dziwnego - to w zasadzie najprostsza, lecz jednocześnie bardzo często używana konstrukcja programistyczna.

Warto jeszcze zapamiętać, że blok instrukcji składający się tylko z jednego polecenia możemy zapisać nawet bez nawiasów klamrowych 1 . Wtedy jednak należy postawić na jego końcu średnik:

if ( warunek ) instrukcja ;

Taka skrócona wersja jest używana często do sprawdzania wartości parametrów funkcji, na przykład:

void Funkcja( int nParametr)
{
// sprawdzenie, czy parametr nie jest mniejszy lub równy zeru -
// jeżeli tak, to funkcja kończy się

if (nParametr <= 0 ) return ;
// ... (reszta funkcji)
}

Fraza else

Prosta wersja instrukcji if nie zawsze jest wystarczająca - nieeleganckie zachowanie naszego przykładowego programu jest dobrym tego uzasadnieniem. Powinien on wszakże pokazać stosowny komunikat również wtedy, gdy użytkownik nie wykaże się chęcią współpracy i nie wprowadzi żądanej liczby. Musi więc uwzględnić przypadek, w którym warunek badany przez instrukcję if (u nas nLiczba > 10 ) nie jest prawdziwy i zareagować nań w odpowiedni sposób.

Naturalnie, można by umieścić stosowny kod po konstrukcji if , ale jednocześnie należałoby zadbać, aby nie był on wykonywany w razie prawdziwości warunku. Sztuczka z dodaniem instrukcji return ; (przerywającej funkcję main() , a więc i cały program) na koniec bloku if zdałaby oczywiście egzamin, lecz straty w przejrzystości i prostocie kodu byłyby zdecydowanie niewspółmierne do efektów :))

Dlatego też C++, jako pretendent do miana nowoczesnego języka programowania, posiada bardziej sensowny i logiczny sposób rozwiązania tego problemu. Jest nim mianowicie fraza else ('w przeciwnym wypadku') - część instrukcji warunkowej if . Korzystająca z niej, ulepszona wersja poprzedniej aplikacji przykładowej może zatem wyglądać chociażby tak:

// Else - blok alternatywny w instrukcji if
void main()
{
int nLiczba;
std::cout << "Wprowadz liczbe wieksza od 10: " ;
std::cin >> nLiczba;
if (nLiczba > 10 )
{
std::cout << "Dziekuje." << std::endl;
std::cout << "Wcisnij dowolny klawisz, by zakonczyc." ;
}
else
{
std::cout << "Liczba " << nLiczba
<< " nie jest wieksza od 10." << std::endl;
std::cout << "Czuj sie upomniany :P" ;
}
getch();
}

Gdy uruchomisz powyższy program dwa razy, w podobny sposób jak poprzednio, w każdym wypadku zostaniesz poczęstowany jakimś komunikatem. Zależnie od wpisanej CAT_PATH ciebie liczby będzie to podziękowanie albo upomnienie :)


Screeny 11 i 12. Dwa warianty działania programu, czyli instrukcje if i else w całej swej krasie :)

Występujący tu blok else jest uzupełnieniem instrukcji if - kod w nim zawarty zostanie wykonany tylko wtedy, gdy określony w if warunek nie będzie spełniony. Dzięki temu możemy odpowiednio zareagować na każdą ewentualność, a zatem nasz program zachowuje się porządnie w obu możliwych przypadkach :)

Funkcja getch() jest w tej aplikacji wywoływana poza blokami warunkowymi, gdyż niezależnie od wpisanej liczby i treści wyświetlanego komunikatu istnieje potrzeba poczekania na dowolny klawisz. Zamiast więc umieszczać tę instrukcję zarówno w bloku if , jak i else , można ją zostawić całkowicie poza nimi.

Czas teraz zaprezentować składnię pełnej wersji instrukcji if , uwzględniającej także blok alternatywny else :

if ( warunek )
{
instrukcje_1
}
else
{
instrukcje_2
}

Kiedy warunek jest prawdziwy, uruchamiane są instrukcje_1 , zaś w przeciwnym przypadku ( else ) - instrukcje_2 . Czy świat widział kiedyś coś równie elementarnego? ;) Nie daj się jednak zwieść tej prostocie - instrukcja warunkowa if jest w istocie potężnym narzędziem, z którego intensywnie korzystają wszystkie programy.

Bardziej złożony przykład

By całkowicie upewnić się, iż znamy i rozumiemy tę szalenie ważną konstrukcję programistyczną, przeanalizujemy ostatnią, bardziej skomplikowaną ilustrację jej użycia. Będzie to aplikacja rozwiązująca równania liniowe - tzn. wyrażenia postaci:

ax + b = 0

Jak zapewne pamiętamy ze szkoły, mogą mieć one zero, jedno lub nieskończenie wiele rozwiązań, a wszystko zależy od wartości współczynników a i b . Mamy zatem duże pole do popisu dla instrukcji if :D

Program realizujący to zadanie wygląda więc tak:

// LinearEq - rozwiązywanie równań liniowych
float fA;
std::cout << "Podaj wspolczynnik a: " ;
std::cin >> fA;
float fB;
std::cout << "Podaj wspolczynnik b: " ;
std::cin >> fB;
if (fA == 0.0 )
{
if (fB == 0.0 )
std::cout << "Rownanie spelnia kazda liczba rzeczywista."
<< std::endl;
else
std::cout << "Rownanie nie posiada rozwiazan." << std::endl;
}
else
std::cout << "x = " << -fB / fA << std::endl;
getch();

Zagnieżdżona instrukcja if wygląda może cokolwiek tajemniczo, ale w gruncie rzeczy istota jej działania jest w miarę prosta. Wyjaśnimy ją za moment.

Najpierw powtórka z matematyki :) Przypomnijmy, iż równanie liniowe ax + b = 0 :

  • posiada nieskończenie wiele rozwiązań, jeżeli współczynniki a i b są jednocześnie równe zeru

  • nie posiada w ogóle rozwiązań, jeżeli a jest równe zeru, zaś b nie

  • ma dokładnie jedno rozwiązanie ( -b/a ), gdy a jest różne od zera

Wynika stąd, że istnieją trzy możliwe przypadki i scenariusze działania programu. Zauważmy jednak, że warunek " a jest równe zeru" jest konieczny do realizacji dwóch z nich - możemy więc go wyodrębnić i zapisać w postaci pierwszej (bardziej zewnętrznej) instrukcji if .

Nadal wszakże pozostaje nam problem współczynnika b - sam fakt zerowej wartości a nie przecież pozwala na obsłużenie wszystkich możliwości. Rozwiązaniem jest umieszczenie instrukcji sprawdzającej b (czyli także if ) wewnątrz bloku if , sprawdzającego a ! Umożliwia to poprawne wykonanie programu dla wszystkich wartości liczb a i b .


Screen 13. Program rozwiązujący równania liniowe

Używamy toteż dwóch instrukcji if , które razem odpowiadają za właściwe zachowanie się aplikacji w trzech możliwych przypadkach. Pierwsza z nich:

if (fA == 0.0 )

kontroluje wartość współczynnika a i tworzy pierwsze rozgałęzienie na szlaku działania programu. Jedna z wychodzących z niego dróg prowadzi do celu zwanego "dokładnie jedno rozwiązanie równania", druga natomiast do kolejnego rozwidlenia:

if (fB == 0.0 )

Ono też kieruje wykonywanie aplikacji albo do "nieskończenie wielu rozwiązań", albo też do "braku rozwiązań" równania - zależy to oczywiście od ewentualnej równości b z zerem.

Operatorem równości w C++ jest == , czyli podwójny znak "równa się" ( = ). Należy koniecznie odróżniać go od operatora przypisania, czyli pojedynczego znaku = . Jeśli omyłkowo użylibyśmy tego drugiego w wyrażeniu będącym warunkiem, to najprawdopodobniej byłby on zawsze albo prawdziwy, albo fałszywy 1 - na pewno jednak nie działałby tak, jak byśmy tego oczekiwali. Co gorsza, można by się o tym przekonać dopiero w czasie działania programu, gdyż jego kompilacja przebiegłaby bez zakłóceń.

Pamiętajmy więc, by w wyrażeniach warunkowych do sprawdzania równości używać zawsze operatora == , rezerwując znak = do przypisywania wartości zmiennym.

***

Ostrzeżeniem tym kończymy nasze nieco przydługie spotkanie z instrukcją warunkową if . Można śmiało powiedzieć, że oto poznaliśmy jeden z fundamentów, na których opiera się działanie wszelkich algorytmów w programach komputerowych. Twoje aplikacje nabiorą przez to elastyczności i będą zdolne do wykonywania mniej trywialnych zadań. jeżeli sumiennie przestudiowałeś ten podrozdział! :))

Instrukcja wyboru switch

Instrukcja switch ('przełącz') jest w pewien sposób podobna do if : jej przeznaczeniem jest także wybór jednego z wariantów kodu podczas działania programu. Pomiędzy obiema konstrukcjami istnieją jednak dość znaczne różnice.

O ile if podejmuje decyzję na podstawie prawdziwości lub fałszywości jakiegoś warunku, o tyle switch bierze pod uwagę wartość podanego wyrażenia . Z tego też powodu może dokonywać wyboru spośród większej liczby możliwości niż li tylko dwóch (prawdy lub fałszu).

Najlepiej widać to na przykładzie:

// Switch - instrukcja wyboru
void main()
{
// (pomijam tu kod odpowiedzialny za pobranie od użytkownika dwóch
// liczb - robiliśmy to tyle razy, że nie powinieneś mieć z tym
// kłopotów :)) Liczby są zapisane w zmiennych fLiczba1 i fLiczba2)

int nOpcja;
std::cout << "Wybierz dzialanie:" << std::endl;
std::cout << "1. Dodawanie" << std::endl;
std::cout << "2. Odejmowanie" << std::endl;
std::cout << "3. Mnozenie" << std::endl;
std::cout << "4. Dzielenie" << std::endl;
std::cout << "0. Wyjscie" << std::endl;
std::cout << "Twoj wybor: " ;
std::cin >> nOpcja;
switch (nOpcja)
{
case 1 : std::cout << fLiczba1 << " + " << fLiczba2 << " = "
<< fLiczba1 + fLiczba2; break ;
case 2 : std::cout << fLiczba1 << " - " << fLiczba2 << " = "
<< fLiczba1 - fLiczba2; break ;
case 3 : std::cout << fLiczba1 << " * " << fLiczba2 << " = "
<< fLiczba1 * fLiczba2; break ;
case 4 :
if (fLiczba2 == 0.0 )
std::cout << "Dzielnik nie moze byc zerem!" ;
else
std::cout << fLiczba1 << " / " << fLiczba2 << " = "
<< fLiczba1 / fLiczba2;
break ;
case 0 : std::cout << "Dziekujemy :)" ; break ;
default : std::cout << "Nieznana opcja!" ;
}
getch();
}

No, to już jest program, co się zowie: posiada szeroką funkcjonalność, prosty interfejs - krótko mówiąc pełen profesjonalizm ;) Tym bardziej więc powinniśmy przejrzeć dokładniej jego kod źródłowy - zważywszy, iż zawiera interesującą nas w tym momencie instrukcję switch .

Zajmuje ona zresztą pokaźną część listingu; na dodatek jest to ten fragment, w którym wykonywane są obliczenia, będące podstawą działania programu. Jaka jest zatem rola tej konstrukcji?


Screen 14. Kalkulator w działaniu

Cóż, nie jest trudno domyśleć się jej - skoro mamy w naszym programie menu, będziemy też mieli kilka wariantów jego działania. Wybranie przez użytkownika jednego z nich zostaje wcielone w życie właśnie poprzez instrukcję switch . Porównuje ona kolejno wartość zmiennej nOpcja (do której zapisujemy numer wskazanej pozycji menu) z pięcioma wcześniej ustalonymi przypadkami. Każdemu z nich odpowiada fragment kodu, zaczynający się od słówka case ('przypadek') i kończący na break ; ('przerwij'). Gdy któryś z nich zostanie uznany za właściwy (na podstawie wartości wspomnianej już zmiennej), wykonywane są zawarte w nim instrukcje. Jeżeli zaś żaden nie będzie pasował, program "skoczy" do dodatkowego wariantu default ('domyślny') i uruchomi jego kod. Ot, i cała filozofia :)

Po tym pobieżnym wyjaśnieniu działania instrukcji switch , poznamy jej pełną postać składniową:

switch ( wyrażenie )
{
case wartość_1 :
instrukcje_1
[ break ; ]
case wartość_2 :
instrukcje_2
[ break ; ]
...
case wartość_n ;
instrukcje_n ;
[ break ; ]
[
default :
instrukcje_domyślne ]
}

Korzystając z niej, jeszcze prościej zrozumieć przeznaczenie konstrukcji switch oraz wykonywane przez nią czynności. Mianowicie, oblicza ona wpierw wynik wyrażenia , by potem porównywać go kolejno z podanymi (w instrukcjach case ) wartościami . Kiedy stwierdzi, że zachodzi równość, skacze na początek pasującego wariantu i wykonuje cały kod aż do końca bloku switch .

Zaraz - jak to do końca bloku? Przecież w naszym przykładowym programie, gdy wybraliśmy, powiedzmy, operację odejmowania, to otrzymywaliśmy wyłącznie różnicę liczb - bez iloczynu i ilorazu (czyli dalszych opcji). Przyczyna tego tkwi w instrukcji break , umieszczonej na końcu każdej pozycji rozpoczętej przez case . Polecenie to powoduje bowiem przerwanie działania konstrukcji switch i wyjście z niej; tym sposobem zapobiega ono wykonaniu kodu odpowiadającego następnym wariantom.

W większości przypadków należy zatem kończyć fragment kodu rozpoczęty przez case instrukcją break - gwarantuje to, iż tylko jedna z możliwości ustalonych w switch zostanie wykonana.

Znaczenie ostatniej, nieobowiązkowej frazy default wyjaśniliśmy sobie już wcześniej. Można jedynie dodać, że pełni ona w switch podobną rolę, co else w if i umożliwia wykonanie jakiegoś kodu także wtedy, gdy żadna z przewidzianych wartości nie będzie zgadzać się z wyrażeniem . Brak tej instrukcji będzie zaś skutkować niepodjęciem żadnych działań w takim przypadku.

***

Omówiliśmy w ten sposób obie konstrukcje, dzięki którym można sterować przebiegiem programu na podstawie ustalonych warunków czy też wartości wyrażeń. Potrafimy więc już sprawić, aby nasze aplikacje zachowywały się prawidłowo niezależnie od okoliczności.

Nie zmienia to jednak faktu, że nadal potrafią one co najwyżej tyle, ile mało funkcjonalny kalkulator i nie wykorzystują w pełni w ogromnych możliwości komputera. Zmienić to może kolejny element języka C++, który teraz właśnie poznamy. Przy pomocy pętli, bo o nich mowa, zdołamy zatrudnić leniuchujący dotąd procesor do wytężonej pracy, która wyciśnie z niego siódme poty ;)

1 Zależałoby to od wartości po prawej stronie znaku równości - jeśli byłaby równa zeru, warunek byłby fałszywy, w przeciwnym wypadku - prawdziwy
1 Zasada ta dotyczy prawie każdego bloku kodu w C++ (z wyjątkiem funkcji)

Kurs C++
| | | |
Copyright © 2006-2013 egrafik.pl | Kontakt | Reklama | Projekty domów
jocker