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:

  1. O inițializare înainte de buclă. Aici, s = 0.
  2. O condiție care poate deveni falsă.
  3. 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 <= 100

Cum alegi

situațiece folosești
Știi exact de câte orifor
Nu știi, și s-ar putea să nu fie nevoie delocwhile
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.
  • suma este long long. Nu știm câte numere vin, iar o sumă lungă depășește int.
  • 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

  • while testează înainte, do-while după. 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-while se termină cu ;.
  • repetă … până când din pseudocod are condiția inversată față de do-while.
  • Verifică întotdeauna cazul în care bucla nu se execută niciodată.