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: 213

Wyrażenia logiczne

Sporą część poprzedniego rozdziału poświęciliśmy na omówienie konstrukcji sterujących, takich jak na przykład pętle. Pozwalają nam one wpływać na przebieg wykonywania programu przy pomocy odpowiednich warunków .

Nasze pierwsze wyrażenia tego typu były bardzo proste i miały dość ograniczone możliwości. Przyszła więc pora na powtórzenie i rozszerzenie wiadomości na ten temat. Zapewne bardzo się z tego cieszysz, prawda? ;)) Zatem niezwłocznie zaczynajmy.

Porównywanie wartości zmiennych

Wszystkie warunki w języku C++ opierają się na jawnym lub ukrytym porównywaniu dwóch wartości. Najczęściej jest ono realizowane poprzez jeden ze specjalnych operatorów porównania , zwanych czasem relacyjnymi . Wbrew pozorom nie są one dla nas niczym nowym, ponieważ używaliśmy ich w zasadzie w każdym programie, w którym musieliśmy sprawdzać wartość jakiejś zmiennej. W poniższej tabelce znajdziesz więc jedynie starych znajomych :)

operator

porównanie jest prawdziwe, gdy

==

lewy argument jest równy prawemu

!=

lewy argument nie jest równy prawemu (jest od niego różny)

>

lewy argument ma większą wartość niż prawy

>=

lewy argument ma wartość większą lub równą wartości prawego

<

lewy argument ma mniejszą wartość niż prawy

<=

lewy argument ma wartość mniejszą lub równą wartości prawego

Tabela 7. Operatory porównania w C++

Dodatkowym ułatwieniem jest fakt, że każdy z tych operatorów ma swój matematyczny odpowiednik - na przykład dla >= jest to ? , dla != mamy ? itd. Sądzę więc, że symbole te nie będą ci sprawiać żadnych trudności. Gorzej może być z następnymi ;)

Operatory logiczne

Doszliśmy oto do sedna sprawy. Nowy rodzaj operatorów, który zaraz poznamy, jest bowiem narzędziem do konstruowania bardziej skomplikowanych wyrażeń logicznych. Dzięki nim możemy na przykład uzależnić wykonanie jakiegoś kodu od spełnienia kilku podanych warunków lub tylko jednego z wielu ustalonych; możliwe są też bardziej zakręcone kombinacje. Zaznajomienie się z tymi operatorami da nam więc pełną swobodę sterowania działaniem programu.

Ubolewam, iż nie mogę przedstawić ciekawych i interesujących przykładowych programów na ilustrację tego zagadnienia. Niestety, choć operatory logiczne są niemal stale używane w programowaniu poważnych aplikacji, trudno o ewidentne przykłady ich głównych zastosowań - może dlatego, że stosuje się je prawie do wszystkiego? :)

Musisz więc zadowolić się niniejszymi, dość trywialnymi kodami, ilustrującymi funkcjonowanie tych elementów języka.

Koniunkcja

Pierwszy z omawianych operatorów, oznaczany poprzez && , zwany jest koniunkcją lub iloczynem logicznym . Gdy wstawimy go między dwoma warunkami, pełni rolę spójnika "i". Takie wyrażenie jest prawdziwe tylko wtedy, kiedy oba te warunki są spełnione .

Operator ten można wykorzystać na przykład do sprawdzania przynależności liczby do zadanego przedziału:

int nLiczba;
std::cout << "Podaj liczbe z zakresu 1-10: " ;
std::cin >> nLiczba;

if (nLiczba >= 1 && nLiczba <= 10 )
std::cout << "Dziekujemy." ;
else
std::cout << "Nieprawidlowa wartosc!" ;

Kiedy dana wartość należy do przedziału < 1 ; 10 > ? Oczywiście wtedy, gdy jest jednocześnie większa lub równa jedynce i mniejsza lub równa dziesiątce. To właśnie sprawdzamy w warunku:

if (nLiczba >= 1 && nLiczba <= 10 )

Operator && zapewnia, że całe wyrażenie ( nLiczba >= 1 && nLiczba <= 10 ) zostanie uznane za prawdziwe jedynie w przypadku, gdy obydwa składniki ( nLiczba >= 1 , nLiczba <= 10 ) będą przedstawiały prawdę. To jest właśnie istotą koniunkcji.

Alternatywa

Drugi rodzaj operacji, zwany alternatywą lub sumą logiczną , stanowi niejako przeciwieństwo pierwszego. O ile koniunkcja jest prawdziwa jedynie w jednym, ściśle określonym przypadku (gdy oba jej argumenty są prawdziwe), o tyle alternatywa jest tylko w jednej sytuacji fałszywa . Dzieje się tak wtedy, gdy obydwa złączone nią wyrażenia przedstawiają nieprawdę .

W C++ operatorem sumy logicznej jest || , co widać na poniższym przykładzie:

int nLiczba;
std::cin >> nLiczba;

if (nLiczba < 1 || nLiczba > 10 )
std::cout << "Liczba spoza przedzialu 1-10." ;

Uruchomienie tego kodu spowoduje wyświetlenie napisu w przypadku, gdy wpisana liczba nie będzie należeć do przedziału < 1 ; 10 > (czyli odwrotnie niż w poprzednim przykładzie). Naturalnie, stanie się tak wówczas, jeśli będzie ona mniejsza od 1 lub większa od 10 . Taki też warunek posiada instrukcja if , a osiągnęliśmy go właśnie dzięki operatorowi alternatywy.

Negacja

Jak można było zauważyć, alternatywa nLiczba < 1 || nLiczba > 10 jest dokładnie przeciwstawna koniunkcji nLiczba >= 1 && nLiczba <= 10 (co jest dość oczywiste - przecież liczba nie może jednocześnie należeć i nie należeć do jakiegoś przedziału :D). Warunki te znacznie różnią się od siebie: stosujemy w nich przecież różne działania logiczne oraz porównania. Moglibyśmy jednak postąpić inaczej.

Aby zmienić sens wyrażenia na odwrotny - tak, żeby było prawdziwe w sytuacjach, kiedy oznaczało fałsz i na odwrót - stosujemy operator negacji ! . W przeciwieństwie do poprzednich, jest on unarny, gdyż przyjmuje tylko jeden argument: warunek do zanegowania.

Stosując go dla naszej przykładowej koniunkcji:

if (nLiczba >= 1 && nLiczba <= 10 )

otrzymalibyśmy wyrażenie:

if (!(nLiczba >= 1 && nLiczba <= 10 ))

które jest prawdziwe, gdy dana liczba nie należy do przedziału < 1 ; 10 > . Jest ono zatem równoważne alternatywnie nLiczba < 1 || nLiczba > 10 , a o to przecież nam chodziło :)

W ten sposób (niechcący ;D) odkryliśmy też jedno z tzw. praw de Morgana. Mówi ono, że zaprzeczenie (negacja) koniunkcji dwóch wyrażeń równe jest alternatywnie wyrażeń przeciwstawnych. A ponieważ nLiczba >= 1 jest odwrotne do nLiczba < 1 , zaś nLiczba <= 10 do nLiczba > 10 , możemy naocznie stwierdzić, że prawo to jest słuszne :)

Czasami więc użycie operatora negacji uwalnia od konieczności przekształcania złożonych warunków na ich przeciwieństwa.

Zestawienie operatorów logicznych

Zasady funkcjonowania operatorów logicznych ujmuje się często w tabelki, przedstawiające ich wartości dla wszystkich możliwych argumentów. Niekiedy nazywa się je tablicami prawd (ang. truth tables ). Nie powinno więc zabraknąć ich tutaj, zatem czym prędzej je przedstawiam:


Tabele 8 i 9. Rezultaty działania operatorów koniunkcji, alternatywy oraz negacji

Oczywiście, nie ma najmniejszej potrzeby, abyś uczył się ich na pamięć (a już się bałeś, prawda? :D). Jeżeli uważnie przeczytałeś opisy każdego z operatorów, to tablice te będą dla ciebie jedynie powtórzeniem zdobytych wiadomości.

Najważniejsze są bowiem proste reguły, rządzące omawianymi operacjami. Powtórzmy je zatem raz jeszcze:

Koniunkcja ( && ) jest prawdziwa tylko wtedy, kiedy oba jej argumenty są prawdziwe .

 

Alternatywa ( || ) jest fałszywa jedynie wówczas, gdy oba jej argumenty są fałszywe .

 

Negacja ( ! ) powoduje zmianę prawdy na fałsz lub fałszu na prawdę.

Łączenie elementarnych wyrażeń przy pomocy operatorów pozwala na budowę dowolnie skomplikowanych warunków, regulujących funkcjonowanie każdej aplikacji. Gdy zaczniesz używać tych działań w swoich programach, zdziwisz się, jakim sposobem mogłeś w ogóle kodować bez nich ;)

Ponieważ operatory logiczne mają niższy priorytet niż operatory porównania, nie ma potrzeby stosowania nawiasów w warunkach podobnych do tych zaprezentowanych. Jeżeli jednak będziesz łączył większą liczbę wyrażeń logicznych, pamiętaj o używaniu nawiasów - to zawsze rozstrzyga wszelkie nieporozumienia i pomaga w uniknięciu niektórych błędów.

Typ bool

Przydatność wyrażeń logicznych byłaby dość ograniczona, gdyby można je było stosować tylko w warunkach instrukcji if i pętli. Zdecydowanie przydałby się sposób na zapisywanie wyników obliczania takich wyrażeń, by móc je potem choćby przekazywać do i z funkcji.

C++ dysponuje rzecz jasna odpowiednim typem zmiennych, nadającym się to tego celu. Jest nim tytułowy bool 1 . Można go uznać za najprostszy typ ze wszystkich, gdyż może przyjmować jedynie dwie dozwolone wartości: prawdę ( true ) lub fałsz ( false ). Odpowiada to prawdziwości lub nieprawdziwości wyrażeń logicznych.

Mimo oczywistej prostoty (a może właśnie dzięki niej?) typ ten ma całe multum różnych zastosowań w programowaniu. Jednym z ciekawszych jest przerywanie wykonywania zagnieżdżonych pętli:

bool bKoniec = false ;
while ( warunek_pętli_zewnętrznej )
{
while ( warunek_pętli_wewnętrznej )
{
kod_pętli

if ( warunek_przerwania_obu_pętli )
{
// przerwanie pętli wewnętrznej
bKoniec = true ;
break ;
}
}

// przerwanie pętli zewnętrznej, jeżeli zmienna bKoniec
// jest ustawiona na true
if (bKoniec) break ;
}

Widać tu klarownie, że zmienna typu bool reprezentuje wartość logiczną - możemy ją bowiem bezpośrednio wpisać jako warunek instrukcji if ; nie ma potrzeby korzystania z operatorów porównania.

W praktyce często stosuje się funkcje zwracające wartość typu bool . Poprzez taki rezultat mogą one powiadamiać o powodzeniu lub niepowodzeniu zleconej im czynności albo sprawdzać, czy dane zjawisko zachodzi, czy nie.

Przyjrzyjmy się takiemu właśnie przykładowi funkcji:

// IsPrime - sprawdzanie, czy dana liczba jest pierwsza
bool
LiczbaPierwsza( unsigned uLiczba)
{
if (uLiczba == 2 ) return true ;
for ( unsigned i = 2 ; i <= sqrt(uLiczba); ++i)
{
if (uLiczba % i == 0 )
return false ;
}
return true ;
}
void main()
{
unsigned uWartosc;
std::cout << "Podaj liczbe: " ;
std::cin >> uWartosc;
if (LiczbaPierwsza(uWartosc))
std::cout << "Liczba " << uWartosc << " jest pierwsza." ;
else
std::cout << "Liczba " << uWartosc<< " nie jest pierwsza." ;
getch();
}

Mamy tu funkcję LiczbaPierwsza() o prostym przeznaczeniu - sprawdza ona, czy podana liczba jest pierwsza 2 , czy nie. Produkuje więc wynik, który może być sklasyfikowany w kategoriach logicznych: prawdy (liczba jest pierwsza) lub fałszu (nie jest). Naturalne jest zatem, aby zwracała wartość typu bool , co też czyni.

Screen 24. Określanie, czy wpisana liczba jest pierwsza

Wykorzystujemy ją od razu w odpowiedniej instrukcji if , przy pomocy której wyświetlamy jeden z dwóch stosownych komunikatów. Dzięki temu, że funkcja LiczbaPierwsza() zwraca wartość logiczną, wszystko wygląda ładnie i przejrzyście :)

Algorytm zastosowany tutaj do sprawdzania "pierwszości" podanej liczby jest chyba najprostszy z możliwych. Opiera się na pomyśle tzw. sita Eratostenesa i, jak widać, polega po prostu na sprawdzaniu po kolei wszystkich liczb jako potencjalnych dzielników, aż do wartości pierwiastka kwadratowego badanej liczby.

Operator warunkowy

Z wyrażeniami logicznymi ściśle związany jest jeszcze jeden, bardzo przydatny i wygodny, operator. Jest on kolejnym z licznych mechanizmów C++, które czynią składnię tego języka niezwykle zwartą.

Mowa tu o tak zwanym operatorze warunkowym ?: Użycie go pozwala na uniknięcie, nieporęcznych niekiedy, instrukcji if . Nierzadko może się nawet przyczynić do poprawy szybkości kodu.

Jego działanie najlepiej zilustrować na prostym przykładzie. Przypuśćmy, że mamy napisać funkcję zwracają większą wartość spośród dwóch podanych 3 . Ochoczo zabieramy się więc do pracy i produkujemy kod podobny do tego:

int max( int nA, int nB)
{
if (nA > nB) return nA;
else return nB;
}

Możemy jednak użyć operatora ?:, a wtedy funkcja przyjmie bardziej oszczędną postać:

int max( int nA, int nB)
{
return (nA > nB ? nA : nB);
}

Znikła nam tu całkowicie instrukcja if , gdyż zastąpił ją nasz nowy operator. Porównując obie (równoważne) wersje funkcji max() , możemy łatwo wydedukować jego działanie.

Wyrażenie zawierające tenże operator wygląda bowiem tak:

warunek ? wartość_dla_prawdy : wartość_dla_fałszu

Składa się więc z trzech części - dlatego ?: nazywany jest czasem operatorem ternarnym , przyjmującym trzy argumenty (jako jedyny w C++).

Jego funkcjonowanie jest nadzwyczaj proste. Sprowadza się do obliczenia warunku oraz podjęcia na jego podstawie odpowiedniej decyzji. Jeśli będzie on prawdziwy, operator zwróci wartość_dla_prawdy , w innym przypadku - wartość_dla_fałszu .

Działalność ta jest w oczywisty sposób podobna do instrukcji if . Różnica polega na tym, że operator warunkowy manipuluje wyrażeniami , a nie instrukcjami. Nie zmienia więc przebiegu programu, lecz co najwyżej wyniki jego pracy.

Kiedy zatem należy go używać? Odpowiedź jest prosta: wszędzie tam, gdzie konstrukcja if wykonuje te same instrukcje w obu swoich blokach, lecz operuje na różnych wyrażeniach . W naszym przykładzie było to zawsze zwracanie wartości przez funkcję (instrukcja return ), jednak sam rezultat zależał od warunku.

***

I to już wszystko, co powinieneś wiedzieć na temat wyrażeń logicznych, ich konstruowania i używania we własnych programach. Umiejętność odpowiedniego stosowania złożonych warunków przychodzi z czasem, dlatego nie martw się, jeżeli na razie wydają ci się one lekką abstrakcją. Pamiętaj, ćwiczenie czyni mistrza!

1 Nazwa pochodzi od nazwiska matematyka George'a Boole'a, twórcy zasad logiki matematycznej (zwanej też algebrą Boole'a).

2 Liczba pierwsza to taka, która ma tylko dwa dzielniki - jedynkę i samą siebie.

3 Tutaj ograniczymy się tylko do liczb całkowitych i typu int .


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