Instrucțiuni repetitive (bucle)
Buclele repetă o instrucțiune de un anumit număr de ori sau cât timp este îndeplinită o anumită condiție. Ele se implementează cu ajutorul cuvintelor cheie while
, do
și for
.
Bucla while
Cea mai simplă buclă este while. Sintaxa ei este:
while (expresie) instructiune
Bucla while pur și simplu repetă
instructiune
cât timp
expresie
este adevărată. Dacă după o execuție a
instructiunii
,
expresie
nu mai este adevărată, bucla se sfârșește și programul continuă exact cu prima instrucțiune de după buclă. De exemplu, să ne uităm puțin la o numărătoare inversă folosind bucla while:
|
// o numaratoare inversa cu while
#include <iostream>
using namespace std;
int main ()
{
int n = 10;
while (n>0) {
cout << n << ", ";
--n;
}
cout << "listare terminata!\n";
}
|
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, listare terminata! |
Prima instrucțiune din
main
îi atribuie lui
n valoarea 10. Acesta este primul număr din numărătoarea inversă. Apoi începe bucla while: dacă această valoare începlinește condiția
n>0
(adică
n este mai mare decât zero), atunci se execută blocul care urmează după condiție și se va repeta atât timp cât condiția (
n>0
) își păstrează valoarea adevărată.
Întregul proces din programul anterior poate fi interpretat conform algoritmului următor (începând cu
main
):
n
ia o valoare
- Se testează condiția lui
while
(n>0
). În acest moment avem două posibilități:
- conditie are valoarea true: se execută instrucțiunea (la pasul 3)
- conditie are valoarea false: se ignoră instrucțiunea și se continuă cu instrucțiunea de după (la pasul 5)
- Execută instrucțiune:
cout << n << ", ";
--n;
(afișează valoarea lui n
și micșorează n
cu 1)
- Sfârșit bloc. Întoarecere automată la pas 2.
- Continuă programul imediat după bloc:
afișează listare terminata!
și programul se termină.
Să subliniem că folosirea unei bucle while presupune că există un punct în care bucla să se termine, adică instrucțiunea trebuie să altereze cumva valorile verificate în condiție,astfel încât să devină falsă la un moment dat. Altfel, bucla s-ar continua la infinit.
În exemplul de mai sus, bucla include
--n
, adică valoarea variabilei (
n
) evaluate în condiție se micșorează cu unu - ceea ce va face ca, după un anumit numă de iterații, condiția (
n>0
) să aiba valoarea fals. Mai exact, după 10 iterații,
n
devine 0, iar condiția nu mai este adevărată și bucla se termină.
Evident, complexitatea acestei bucle este banală pentru calculator, iar numărătoarea inversă se execută aproape instantaneu, fără niciun fel de întârziere între elementele contorului (dacă vă interesează, vedeți
sleep_for
pentru un exemplu de numărătoare cu întârzieri).
Bucla do-while
Foarte asemănătoare este bucla do-while, cu sintaxa:
do instructiune while (conditie);
Se comportă ca și o buclă while, exceptând faptul că se evaluează
conditie
după ce se execută
instrucțiune
și nu înainte, ceea ce garantează execuția cel puțin o dată a
instructiunii
, chiar și atunci când
conditir
nu este îndeplinită deloc. De exemplu, următorul program redă pe ecran orice text introduce utilizatorul, până când acesta introduce la revedere:
|
// program ecou
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string sir;
do {
cout << "Tastati un text: ";
getline (cin,sir);
cout << "Ati tastat: " << sir << '\n';
} while (sir != "la revedere");
}
|
Tastati un text: hello
Ati tastat: hello
Tastati un text: cine-i acolo?
Ati tastat: cine-i acolo?
Tastati un text: la revedere
Ati tastat: la revedere |
În general, alegem bucla do-while în loc de bucla while când
instructiune
trebuie executată cel puțin o dată. Așadar, valoarea pe care o are condiția la sfârșitul buclei este determinată chiar de instrucțiune. În exemplul anterior, de datele furnizate de utilizator în interiorul blocului depinde sfârșitul buclei. Și totuși, chiar dacă utilizatorul dorește terminarea buclei cât mai repede tastând
la revedere, blocul din buclă trebuie executat cel puțin o dată pentru a-i permite tastarea, deci condiția va putea fi îndeplinită numai după executare.
Bucla for
Bucla
for
este proiectată să repete de un anumit număr de ori. Sintaxa ei este:
for (initializari; conditie; actualizari) instructiune;
Ca și bucla while, aceasta repetă
instructiune
cât timp este îndeplinită
conditie
. Dar, în plus, bucla for prevede ce să conțină anumite zone de memorie prin
initializari
și expresii de tip
actualizari
, executate înainte de prima execuție a buclei, respectiv după fiecare repetare a acesteia. De aceea, este foarte utilă folosirea de variabile contor în
conditie
.
Mecanismul de execuție este următorul:
- Se execută
initializari
. În general, aici se declară o variabilă contor și i se setează valoarea inițială. Acest cod este executat o singură dată, la începutul buclei.
- Se evaluează
conditie
. Dacă este îndeplinită, bucla va continua; dacă nu, se va termina și se sare peste instructiune
direct la pasul 5.
- Se execută
instructiune
. Ca de obicei, ea poate fi o singură instrucțiune sau un bloc cuprins între acolade { }
.
- Se execută
actualizari
și bucla revine la pasul 2.
- Bucla se termină: programul continuă cu instrucțiunea imediat următoare.
Iată și exemplul numărătorii inverse folosind o buclă for:
|
// numărătoare inversa folosind bucla for
#include <iostream>
using namespace std;
int main ()
{
for (int n=10; n>0; n--) {
cout << n << ", ";
}
cout << "listare terminata!\n";
}
|
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, listare terminata! |
Cele trei câmpuri dintr-o buclă for sunt opționale. Ele pot fi lăsate necompletate, dar semnul punct și virgulă dintre ele trebuie să apară (este obligatoriu). De exemplu,
for (;n<10;)
este o buclă fără
initializari sau
actualizari (echivalent cu o buclă while); și
for (;n<10;++n)
este o buclă cu
actualizari, dar fără
initializari (este posibil ca variabilei să i se fi atribuit deja o valoare înainte de buclă). O buclă fără
conditie este echivalentă cu o buclă a cărei condiție are întotdeauna valoarea
true
(adică o buclă infinită).
Deoarece fiecare câmp este executat la un anumit moment de timp al buclei, poate ar fi utile mai multe expresii de tip
initializari,
conditie sau
actualizari. Din păcate, acestea nu sunt instrucțiuni, ci doar simple expresii, deci nu pot fi înlocuite cu un bloc. Ca și expresiile, ele pot conține operatorul virgulă (
,
): acesta este un separator de expresii și poate fi utilizat acolo unde se așteaptă o singură expresie, dacă dorim să scriem mai mult. De exemplu, am putea să îl folosim într-o buclă for pentru a gestiona două variabile contor, pe care să le inițializăm și să le actualizăm:
1 2 3 4
|
for ( n=0, i=100 ; n!=i ; ++n, --i )
{
// orice aici...
}
|
Această buclă s-ar executa de 50 de ori dacă nici
n
nici
i
nu ar fi modificate în interiorul buclei:
n
începe cu valoarea 0 și
i
cu 100, condiția este
n!=i
(adică
n
este diferit de
i
). Deoarece
n
crește cu unu și
i
scade cu unu la fiecare repetiție, condiția buclei va avea valoarea fals după a 50-a repetiție, când atât
n
cât și
i
vor fi egale cu 50.
Buclă for pe mulțimi ordonate
Bucla for are și o altă sintaxă, care se folosește numai cu mulțimi ordonate:
for ( declaratie : multime ) instructiune;
Acest tip de buclă for repetă pentru toate elementele din
multime
, unde
declaratie
declară o variabilă ce poate lua valori ale elementelor din această mulțime. Mulțimile ordonate sunt secvențe de elemente, inclusiv tablouri, containere și alte tipuri care suportă funcțiile
begin și
end; cele mai multe dintre aceste tipuri nu au fost încă prezentate în acest tutorial, dar suntem pregătiți cu cel puțin un tip de mulțime ordonată: string-uri, care sunt secvențe de caractere.
Un exemplu de buclă for pe mulțimi ordonate pentru string-uri:
|
// bucla for pe multime ordonata
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string sir {"Hello!"};
for (char c : sir)
{
std::cout << "[" << c << "]";
}
std::cout << '\n';
}
|
[H][e][l][l][o][!] |
Observăm că înainte de două puncte (
:
) în bucla for avem declarația unei variabile de tip
char
(elementele dintr-un string sunt de tip
char
). Apoi, vom folosi această variabilă,
c
, în blocul de instrucțiuni pentru a reprezenta valoarea fiecărui element din mulțimea ordonată.
Aceasta este o buclă automatizată și nu necesită declarea explicită a vreunei variabile contor.
Buclele pe mulțimi ordonate pot deduce tipul elementelor cu
auto
. Mai exact, bucla de mai sus poate fi scrisă:
1 2
|
for (auto c : sir)
std::cout << "[" << c << "]";
|
Aici, tipul lui
c
este dedus automat ca fiind tipul elementelor din
sir
.