Instrucțiunile repetitive în C++: for
, while
, do while
Ca orice alt limbaj de programare, C++ are un set de instrucțiuni repetitve. Instrucțiunile repetitive au rolul de a repeta execuția unui set de instrucțiuni pentru un anumit număr de ori, sau cât timp o condiție dată este îndeplinită. În C++, aceste instrucțiuni sunt for
, while
și do while
.
Instrucțiunea while
Instrucțiunea while
este cea mai simplă instrucțiune repetitivă din limbajul C++. Ea are rolul de a executa un set de instrucțiuni cât timp o condiție este adevărată.
Sintaxă while
while (conditie) instructiune / block de instructiuni
Semantică while
Mai intâi, se testează dacă conditie
este adevărată. Dacă da, se execută instructiune
, și procesul o ia de la capăt. Dacă nu, se iese din while
.
Exemple while
int n = 5;while (n < 10) n += 2;
Inițial, n
este 5
. Cum 5 < 10
, se execută instrucțiunea n += 2
, n
devenind 7
. Și 7 < 10
, așa că n
ajunge la valoarea 9
. Se intră din nou în while, iar apoi n
devine 11
. De data asta 11 < 10
este fals, așa că se iese din while
și se continuă cu execuția următoarei instrucțiuni din program.
while (n--) { cin >> x; cout << x << '\n';}
Cred că este destul de clar că, la fiecare pas, se citește și se afișează câte un număr (reținut în variabila x
). Mai interesant este să ne dăm seama de câte ori se intră în while
, pentru că avem o condiție mai neobișnuită. Ei bine, de fiecare dată când se evaluează n--
, de fapt se testează dacă n
este nenul. Apoi, indiferent de rezultat, n
se decrementează. (Mai multe detalii despre operatorul sufix --
aici.) Dacă n
este 0
, se iese din while
. Se observă că după while
, n
are valoarea -1
.
while (true) cout << 1;
Acest while
va afișa cifra 1
de o infinitate de ori (cât timp adevărul este adevărat).
Instrucțiunea do while
Instrucțiunea do while
seamănă foarte bine cu while
, diferența majoră fiind că do while
testează condiția după ce se execută setul de instrucțiuni.
Sintaxă do while
do instructiune / block de instructiuniwhile (conditie);
Semantică do while
Se începe prin execuția instrucțiunii instructiune
. Apoi, se testează conditie
. În cazul în care condiția este îndeplinită, procesul se reia. Dacă nu, se iese din do while
. Se poate observa că setul de instrucțiuni este executat cel puțin o dată, datorită poziționării testului de condiție.
Atenție! Din păcate, în cadrul conditie
nu se pot folosi variabile locale declarate în block-ul de instrucțiuni subordonat do while
-ului.
Exemplu do while
do { cin >> x; sum += x;} while (x != -1);
În acest exemplu, se vor citi numere și se vor aduna la suma sum
până când se introduce valoarea -1
(inclusiv).
Instrucțiunea for
Instrucțiunea for
este folosită de obicei pentru a itera printre numerele dintr-un interval în ordine crescătoare sau descrescătoare. Însă, limbajul C a inovat cu adevărat instrucțiunea for
, făcând-o mult mai flexibilă și mai practică.
Sintaxă for
for (instructiune1; conditie; instructiune2) instructiune / block de instructiuni
Este bine de știut că oricare din cei trei „parametri” ai structurii for
poate lipsi, însă cele două caractere ;
sunt obligatorii, pentru a-i separa. Dacă conditie
lipsește, se consideră că valoarea sa de adevăr este true
întotdeauna.
Semantică for
- Se execută
instructiune1
. Aceasta este o instrucțiune de inițializare, ce se execută o singură dată. De obicei, inițializează o variabilă contor (iterator) cu o anumită valoare, de la care se începe iterația. - Se testează
conditie
. Dacă aceasta este adevărată, se continuă cu pasul următor. Dacă nu, se iese imediat dinfor
și programul continuă cu instrucțiunile următoare. - Se execută
instructiune
. - Se execută
instructiune2
, care de obicei incrementează sau decrementează iteratorul.
Exemple for
Exemplul 1.
Afișarea textului
"InfoGenius\n"
de618
ori
for (i = 0; i < 618; i++) cout << "InfoGenius\n";
Observăm că pentru acest for
, i
-ul (i
de la iterator) trebuie declarat (ca orice altă variabilă) mai sus. În C++ s-a adăugat posibilitatea declarării iteratorilor locali chiar în for
, ceea ce în C nu era posibil. Pe de o parte este avantajos, pentru că ne ajută să menținem un program cât mai modularizat, dar are și dezavantaje despre care vom discuta imediat. Până una alta, cam așa arată versiunea C++ a for
-ului de mai sus:
for (int i = 0; i < 618; i++) cout << "InfoGenius\n";
cout << i << '\n'; // eroare// i-ul este local în for. După ieșirea din for, nu// ne mai putem atinge de variabila i declarată în for.
Acesta este un exemplu în care variabila contor este folosită doar pentru a menține numărul curent de pași. Așa că, un for
ce mergea de la 1
până la 618
(inclusiv) ar fi avut exact același efect.
for (i = 1; i <= 618; i++) cout << "InfoGenius\n";
Chiar și un for
ce itera de la 618
la 1
ar fi fost bun în această situație.
for (i = 618; i >= 1; i--) cout << "InfoGenius\n";
Exemplul 2.
Calcularea sumei a
n
numere date
Acesta este un bun exemplu unde putem folosi un block de instrucțiuni subordonat for
-ului. La fiecare pas vom citi valoarea numărului curent în x
, iar apoi vom actualiza suma.
cin >> n;for (i = 0; i < n; i++) { cin >> x; sum += x;}
Exemplul 3.
Căutarea celui mai mare număr divizibil cu
10
mai mic decâtn
// i se decrementează cât timp i nu e divizibil cu 10:for (i = n; i % 10; i--);cout << i << '\n';
La fiecare pas acest for
execută instrucțiunea vidă (;
). În problemele cu vectori, for
-uri de genul ăsta sunt foarte utile. În plus, se mai poate observa ceva important. Ăsta e unul din for
-urile unde suntem obligați să nu declarăm iteratorul local. Dacă am face asta, nu l-am mai putea afișa când ieșim din for
.
Exemplul 4.
Parcurgerea unui interval din ambele direcții simultan
for (st = 1, dr = 10; st < dr; st++, dr--) {}
Din nou, un exemplu practic se poate găsi în articolul Probleme simple cu vectori în C++. Am vrut să vă arăt că for
-ul este al doilea context în care operatorul de concatenare a instrucțiunilor (,
) este cel mai folosit. Primul este declararea a mai multe variabile de același tip printr-o singură instrucțiune. Ei bine, le putem combina
// st și dr vor fi variabile locale în forfor (int st = 1, dr = 10; st < dr; st++, dr--) {}
Un alt lucru bun de observat este că aici am folosit acolade fără instrucțiuni între ele, în loc de ;
. Este exact același lucru.
Exemplul 5.
Afișarea unui pătrat de lungime
10
format din caractere'.'
for (int i = 0; i < 10; i++) { // iterăm printre linii for (int j = 0; j < 10; j++) // iterăm printre coloane cout << '.'; cout << '\n';}
Acest exemplu arată că putem avea un for
în alt for
(for
-uri imbricate). Totuși, este o problemă cu secvența de cod de mai sus. La fiecare pas din primul for
, când se intră în al doilea, se declară variabila j
. Deci j
va fi declarat de 10
ori. Dacă declaram ambele variabile contor înaintea for
-urilor, j
ar fi fost declarat o singură dată, deci ar fi o soluție ceva mai bună. La concursuri și olimpiade nu risc să-i declar local când e vorba de for
-uri imbricate, chiar dacă nu cred că asta a făcut vreodată departajarea între două punctaje prin timpul de execuție. Dar când lucrez acasă probleme de info, prefer iteratorii locali mereu, pentru că sunt mult mai comozi.
Încă ceva interesant despre iteratorii locali. Secvența următoare de cod funcționează la fel de bine ca cea de mai sus. Când se iese din al doilea for
și se continuă cu i++
din primul, al doilea i
deja nu mai există, așa că nu va intra în conflict cu primul, generând un rezultat corect.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) cout << '.'; cout << '\n';}
Exemplul 6.
Formarea unui ciclu infinit
for (; ; );
Inițial, acest for
nu face nimic. Apoi, pentru totdeauna, nu face nimic de câte două ori. Acesta este un exemplu de ciclu infinit, echivalentul lui while (true)
și al lui do ; while (true);
.
Instrucțiunile break
și continue
Adeseori, pentru a nu scrie condiții prea lungi în instrucțiunile repetitive, folosim break
și switch
. Ele pot fi utilizate atât în for
, cât și în (do) while
. Instrucțiunea break
face programul să iasă imediat din structura repetitivă curentă, fără să mai testeze vreo condiție logică. În schimb, instrucțiunea continue
are rolul de a trece direct la pasul următor din instrucțiunea repetitivă. Mai precis, te trimite la finalul block-ului de cod curent. Am folosit vectori pentru a ilustra câte un exemplu clar cu cele două instrucțiuni.
Exemplu break
for (i = 0; i < n; i++) if (v[i] == 618) { cout << "DA\n"; break; }
În acest exemplu, se caută valoarea 618
în vectorul v
, și se afișează "DA\n"
doar dacă aceasta există. Când am găsit-o, am dat un break
, pentru că nu are sens să continuăm căutarea printre următoarele elemente. Iată și varianta în care nu se folosește break
:
bool ok = false;for (i = 0; i < n && !ok; i++) if (v[i] == 618) ok = true;if (ok) cout << "DA\n";
Exemplu continue
for (i = 0; i < n; i++) { if (v[i] == 618) continue; sum += v[i];}
Aici adunăm la sum
doar elementele din v
diferite de 618
. Desigur, problema se putea rezolva în mai puține linii de cod, fără continue
. De obicei, continue
este folosit ca să dăm repede la o parte cazurile care nu ne interesează.
Bonus: Range-based for
Acesta este un tip mai special de for
, introdus în C++11, special pentru STL. Cel mai probabil n-ați auzit de el la școală El are rolul de a itera printre elementele unui container STL, ajutându-ne să scriem mult mai puțin cod decât cu un for
obișnuit (în special pentru containere ca map
). Sintaxa este următoarea:
for (tipIterator i : container) instructiune / block de instructiuni
Iteratorul trebuie să fie neapărat declarat local în for
. Iată cum funcționează un range-based for
pentru std::vector
:
vector<int> v = {6, 1, 8};for (int i : v) cout << i;// Se afișează 618.
Acesta este echivalentul a:
vector<int> v = {6, 1, 8};for (int i = 0; i < int(v.size()); i++) cout << v[i];// Se afișează 618.
De multe ori, în astfel de for
-uri, iteratorul este declarat folosind auto
. Motivul este că, din nou, te scutește de ceva tastat la containeri ca map
, unde iteratorii sunt mai complicați. Voi scrie mai detaliat despre acest tip de for
în articolele următoare despre STL.
Pe cât de util este range-based for
-ul, pe atât de atenți trebuie să fim la concursuri dacă putem folosi facilitățile C++11. Evaluatorul învechit de pe site-ul .campion, de exemplu, nu acceptă așa ceva.
Sper că n-am ratat nimic important despre instrucțiunile repetitive. Puteți găsi mai multe aplicații cu acestea în articolul Probleme cu elementele de bază ale limbajului C++. De asemenea, o sursă bună de antrenament este PbInfo. Dacă aveți vreo întrebare legată de instrucțiunile repetitive din C++, lăsați un comentariu mai jos și vă voi ajuta