while și do-while
Buclele pentru când nu știi de câte ori: condiția de oprire, capcana buclei infinite și diferența dintre testul dinainte și cel de după.
while
while (condiție) {
// se repetă cât timp condiția e adevărată
}Condiția se testează înainte de fiecare execuție a corpului. Dacă este falsă de la început, corpul nu se execută niciodată.
int n;
cin >> n;
int s = 0;
while (n > 0) {
s += n % 10;
n /= 10;
}
cout << s;Pentru n = 0, bucla nu pornește deloc și se afișează 0. Întâmplător, chiar aceasta este suma cifrelor lui 0. Cazurile limită verifică-le mereu, nu le presupune.
Trei lucruri care trebuie să existe
Orice while corect are:
- O inițializare înainte de buclă. Aici,
s = 0. - O condiție care poate deveni falsă.
- O modificare în corp care duce spre momentul în care condiția devine falsă. Aici,
n /= 10.
Dacă lipsește al treilea punct, bucla nu se termină:
int i = 1;
while (i <= 10) {
cout << i;
// am uitat i++
}Programul scrie 1 la nesfârșit. La bac, pe hârtie, greșeala se vede imediat dacă urmărești două-trei iterații în tabel.
do-while
do {
// se execută cel puțin o dată
} while (condiție);Testul este după corp, deci corpul se execută cel puțin o dată, indiferent de condiție. Observă punctul și virgula de la final. Aici este obligatoriu, spre deosebire de while.
Se folosește când o operație trebuie făcută înainte să ai ce testa:
int x;
do {
cout << "Dă un număr între 1 și 100: ";
cin >> x;
} while (x < 1 || x > 100);Nu ai cum să testezi x înainte să-l citești o dată. Cu while ar trebui să duplici citirea, înainte și în corp.
În pseudocod îi corespunde repetă … până când. Atenție la o diferență de logică ușor de ratat: do-while continuă cât timp condiția este adevărată, iar repetă continuă până când condiția devine adevărată. Condițiile sunt deci inversate una față de cealaltă.
┌repetă
│ citește x
└până când x >= 1 și x <= 100Cum alegi
| situație | ce folosești |
|---|---|
| Știi exact de câte ori | for |
| Nu știi, și s-ar putea să nu fie nevoie deloc | while |
| Nu știi, dar sigur se face măcar o dată | do-while |
Toate trei sunt interschimbabile: orice for se poate scrie ca while. O alegi pe cea care arată mai limpede ce vrei să faci.
Două tipare care revin mereu
Prelucrarea cifrelor
while (n > 0) {
int c = n % 10; // ultima cifră
// fac ceva cu c
n /= 10; // o tai
}Cifrele vin de la dreapta la stânga. Dacă ai nevoie de ele în ordinea firească, fie construiești răspunsul invers, fie lucrezi cu oglinditul numărului.
Căutarea cu oprire
Când cauți ceva și te poți opri de îndată ce l-ai găsit:
int d = 2;
bool prim = true;
while (d * d <= n && prim) {
if (n % d == 0) prim = false;
d++;
}Condiția are două părți. Una mărginește căutarea, cealaltă spune „nu am terminat încă". E mai curat decât să ieși din buclă la mijloc.
Un exemplu complet
Se citesc numere întregi până la întâlnirea valorii 0. Să se afișeze câte au fost strict pozitive și media lor.
#include <iostream>
using namespace std;
int main() {
int x, cate = 0;
long long suma = 0;
cin >> x;
while (x != 0) {
if (x > 0) {
cate++;
suma += x;
}
cin >> x;
}
if (cate > 0)
cout << cate << " " << (double)suma / cate;
else
cout << "niciun numar pozitiv";
return 0;
}Trei alegeri importante:
- Citire înainte de buclă și la sfârșitul corpului. Altfel terminatorul 0 ar fi prelucrat.
sumaestelong long. Nu știm câte numere vin, iar o sumă lungă depășeșteint.- Se testează
cate > 0înainte de împărțire. Dacă nu a venit niciun număr pozitiv, am împărți la zero.
De reținut
whiletestează înainte,do-whiledupă. De asta depinde dacă corpul se execută măcar o dată.- Fiecare buclă are inițializare, condiție și ceva care schimbă condiția. Fără ultima, bucla devine infinită.
do-whilese termină cu;.repetă … până cânddin pseudocod are condiția inversată față dedo-while.- Verifică întotdeauna cazul în care bucla nu se execută niciodată.