Schimbări ale Structurilor Repetitive: Transformarea Din “Cât Timp” În “Repetă Până Când” (și Înapoi!) – Materie BAC

Bun, hai să vorbim despre unul dintre cele mai practice și mai puțin înțelese transformări în programare: Cum schimbi o structură repetitivă într-o alta. Nu e doar despre rescriere (cuvânt plictisitor) și echivalențe. E despre cum vezi aceeași problemă din două perspective diferite, cum un algoritm poate fi scris în două moduri diferite care duc la același rezultat. E o abilitate atât de importantă încât dacă ai stăpâni-o, ai putea rescrie orice buclă în orice altă buclă!

1. De Ce Să Schimbi Structurile Repetitive? “Aceeași Mașină, Două Butoane Diferite”
Gândește-te la structurile repetitive ca la același călător care poate lua două rute diferite spre aceeași destinație:

  • Ruta 1 (cât timp): “Merg cât timp drumul nu e blocat, dacă e blocat de la început, mă întorc”
  • Ruta 2 (repetă până când): “Merg o dată sigur, apoi continui până când drumul e blocat”

Ambele ajung în același loc, dar:

  1. Pornesc diferit
  2. Verifică condiția în momente diferite
  3. Au riscuri diferite

De ce să înveți să le schimbi?

  • Pentru că uneori cerința specifică un anumit tip de buclă
  • Pentru că altă dată vezi mai clar soluția într-o formă
  • Pentru că vrei să demonstrezi că înțelegi logica din spate
  • Pentru că la examen te pot întreba să transformi una în alta!

2. Cele Două “Frați” Opuși: WHILE vs REPEAT-UNTIL

WHILE (cât timp) – “Verifică înainte să intri”

cât timp (condiție ADEVĂRATĂ) execută
    instrucțiuni
sfârșit cât timp

REPEAT-UNTIL (repetă până când) – “Intră sigur o dată, verifică după”

repetă
    instrucțiuni
până când (condiție ADEVĂRATĂ)

OBSERVAȚIE CRUCIALĂ:

  • WHILE: Continuă CÂT TIMP condiția e adevărată
  • REPEAT-UNTIL: Continuă PÂNĂ CÂND condiția devine adevărată
  • SUNT OPUSE LOGIC!
  • while(x < 5) = repetă cât timp x este mai mic decât 5
  • repeat ... until x >= 5 = repetă până când x devine mai mare sau egal cu 5

3. Transformarea de BAZĂ: Cum Schimbi WHILE în REPEAT-UNTIL

REGULA de AUR:
Pentru a transforma un while într-un repeat-until:

  1. Inversează condiția (neg-o!)
  2. Asigură-te că instrucțiunile se execută cel puțin o dată
  3. Pune bucla într-un if dacă nu vrei execuție garantată

Exemplul 1: Numărătoare descrescătoare

// WHILE - versiunea originală
#include <iostream>
using namespace std;

int main() {
    int numar = 10;

    cout << "Lansare in: ";
    while (numar > 0) {  // Cât timp numar > 0
        cout << numar << "... ";
        numar--;
    }
    cout << "POC! Lansat!\n";

    return 0;
}
// REPEAT-UNTIL - versiunea transformată
#include <iostream>
using namespace std;

int main() {
    int numar = 10;

    cout << "Lansare in: ";

    // TRANSFORMARE:
    // while (numar > 0) devine:
    // do { ... } while (!(numar <= 0)) care e echivalent cu
    // do { ... } while (numar > 0) - STAI, NU! 
    // Urmărește cu atenție...

    // CORECT: while(numar > 0) → do-while cu condiția opusă
    if (numar > 0) {  // Verifică dacă ar intra în while
        do {
            cout << numar << "... ";
            numar--;
        } while (numar > 0);  // ATENȚIE: condiția rămâne aceeași pentru do-while
        // Dar pentru repeat-until în Pascal ar fi: until numar <= 0
    }

    cout << "POC! Lansat!\n";

    return 0;
}

Să explicăm transformarea pentru PSEUDOCOD (Pascal-like):

// WHILE original
numar ← 10
scrie "Lansare in: "
cât timp numar > 0 execută
    scrie numar, "... "
    numar ← numar - 1
sfârșit cât timp
scrie "POC! Lansat!"
// REPEAT-UNTIL transformat
numar ← 10
scrie "Lansare in: "

// TRUC: Mai întâi verificăm dacă ar intra în while
dacă numar > 0 atunci
    repetă
        scrie numar, "... "
        numar ← numar - 1
    până când numar <= 0  // CONDIȚIA OPUSĂ!
sfârșit dacă

scrie "POC! Lansat!"

Regula de transformare clară:

CÂT TIMP (condiție) execută
    instrucțiuni
SFÂRȘIT CÂT TIMP

DEVINE

dacă (condiție) atunci
    repetă
        instrucțiuni
    până când NOT(condiție)
sfârșit dacă

4. Transformarea INVERSĂ: REPEAT-UNTIL în WHILE

REGULA de AUR inversă:
Pentru a transforma un repeat-until într-un while:

  1. Scrii instrucțiunile o dată înainte de while
  2. Pui aceleași instrucțiuni în while
  3. Condiția rămâne aceeași (pentru do-while în C++)
    sau se inversează (pentru repeat-until în Pascal)

Exemplul 2: Meniu interactiv

// REPEAT-UNTIL (do-while) - versiunea originală
#include <iostream>
using namespace std;

int main() {
    char raspuns;

    do {
        cout << "Vrei sa joci? (d/n): ";
        cin >> raspuns;

        if (raspuns == 'd' || raspuns == 'D') {
            cout << "Super! Hai sa jucam...\n";
            // ... cod joc ...
        }
    } while (raspuns == 'd' || raspuns == 'D');  // Cât timp răspuns e 'd'

    cout << "Ok, pa pa!\n";

    return 0;
}
// WHILE - versiunea transformată
#include <iostream>
using namespace std;

int main() {
    char raspuns;

    // TRANSFORMARE din do-while în while:
    // 1. Execută instrucțiunile o dată înainte
    cout << "Vrei sa joci? (d/n): ";
    cin >> raspuns;

    if (raspuns == 'd' || raspuns == 'D') {
        cout << "Super! Hai sa jucam...\n";
        // ... cod joc ...
    }

    // 2. Pui while cu aceleași instrucțiuni
    while (raspuns == 'd' || raspuns == 'D') {
        cout << "Vrei sa joci? (d/n): ";
        cin >> raspuns;

        if (raspuns == 'd' || raspuns == 'D') {
            cout << "Super! Hai sa jucam...\n";
            // ... cod joc ...
        }
    }

    cout << "Ok, pa pa!\n";

    return 0;
}

DAR! Există o problemă: COD DUPLICAT!
Am scris aceleași instrucțiuni de 2 ori. Iată soluția elegantă:

// WHILE - versiunea transformată CORECTĂ
#include <iostream>
using namespace std;

int main() {
    char raspuns = 'd';  // FORȚĂM prima intrare în buclă

    // Folosim o variabilă care ne forțează să intrăm măcar o dată
    while (raspuns == 'd' || raspuns == 'D') {
        cout << "Vrei sa joci? (d/n): ";
        cin >> raspuns;

        if (raspuns == 'd' || raspuns == 'D') {
            cout << "Super! Hai sa jucam...\n";
            // ... cod joc ...
        }
    }

    cout << "Ok, pa pa!\n";

    return 0;
}

Sau pentru PSEUDOCOD (Pascal):

// REPEAT-UNTIL original
repetă
    scrie "Vrei sa joci? (d/n): "
    citește raspuns

    dacă raspuns = 'd' sau raspuns = 'D' atunci
        scrie "Super! Hai sa jucam..."
    sfârșit dacă
până când raspuns <> 'd' și raspuns <> 'D'

scrie "Ok, pa pa!"
// WHILE transformat
raspuns ← 'd'  // Inițializare forțată

cât timp raspuns = 'd' sau raspuns = 'D' execută
    scrie "Vrei sa joci? (d/n): "
    citește raspuns

    dacă raspuns = 'd' sau raspuns = 'D' atunci
        scrie "Super! Hai sa jucam..."
    sfârșit dacă
sfârșit cât timp

scrie "Ok, pa pa!"

5. Transformări Complexe: Cu Break și Continue

Exemplul 3: Căutare cu break

// WHILE cu break - original
#include <iostream>
using namespace std;

int main() {
    int numar;
    bool gasit = false;

    cout << "Ghiceste numarul magic (1-100): ";

    while (!gasit) {  // Cât timp NU e gasit
        cin >> numar;

        if (numar == 42) {
            cout << "BRAVO! Ai ghicit!\n";
            gasit = true;  // Sau break;
        } else if (numar < 42) {
            cout << "Prea mic! Mai incearca: ";
        } else {
            cout << "Prea mare! Mai incearca: ";
        }
    }

    return 0;
}
// REPEAT-UNTIL transformat cu flag
#include <iostream>
using namespace std;

int main() {
    int numar;
    bool gasit = false;

    cout << "Ghiceste numarul magic (1-100): ";

    if (!gasit) {  // Echivalentul verificării inițiale din while
        do {
            cin >> numar;

            if (numar == 42) {
                cout << "BRAVO! Ai ghicit!\n";
                gasit = true;
            } else if (numar < 42) {
                cout << "Prea mic! Mai incearca: ";
            } else {
                cout << "Prea mare! Mai incearca: ";
            }
        } while (!gasit);  // Continuă până când e găsit
    }

    return 0;
}

6. Cazuri Speciale: Transformări cu FOR

FOR poate fi transformat în WHILE și invers!

// FOR original
#include <iostream>
using namespace std;

int main() {
    for (int i = 1; i <= 10; i++) {
        cout << i << " ";
    }

    return 0;
}
// WHILE transformat
#include <iostream>
using namespace std;

int main() {
    int i = 1;  // Inițializare

    while (i <= 10) {  // Condiție
        cout << i << " ";
        i++;           // Incrementare
    }

    return 0;
}
// REPEAT-UNTIL transformat (cu atenție!)
#include <iostream>
using namespace std;

int main() {
    int i = 1;

    if (i <= 10) {  // Verifică dacă s-ar executa măcar o dată
        do {
            cout << i << " ";
            i++;
        } while (i <= 10);
    }

    return 0;
}

7. Tabel de Transformare COMPLET

Structura OriginalăStructura TransformatăObservații
while(cond) { instr; }if(cond) { do { instr; } while(cond); }Atenție la duplicarea condiției!
do { instr; } while(cond);instr; while(cond) { instr; }Duplicare de cod! Mai bine: inițializezi variabilele să forțeze prima intrare
for(init; cond; inc) { instr; }init; while(cond) { instr; inc; }Transformare directă
repeat instr until cond; (Pascal)instr; while(!cond) { instr; }Condiția se inversează!

8. EXERCIȚII Practice de Transformare

Exercițiul 1: Transformă WHILE în REPEAT-UNTIL

// EXERCIȚIU: Transformă acest while în repeat-until
#include <iostream>
using namespace std;

int main() {
    int suma = 0;
    int numar;

    cout << "Introdu numere (0 pentru stop): ";
    cin >> numar;

    while (numar != 0) {
        suma += numar;
        cin >> numar;
    }

    cout << "Suma este: " << suma << endl;

    return 0;
}
// SOLUȚIE: Transformarea corectă
#include <iostream>
using namespace std;

int main() {
    int suma = 0;
    int numar;

    cout << "Introdu numere (0 pentru stop): ";
    cin >> numar;

    // WHILE devine:
    if (numar != 0) {  // Verifică condiția inițială
        do {
            suma += numar;
            cin >> numar;
        } while (numar != 0);  // Condiția rămâne aceeași
    }

    cout << "Suma este: " << suma << endl;

    return 0;
}

Exercițiul 2: Transformă REPEAT-UNTIL în WHILE

// EXERCIȚIU în pseudocod (Pascal-style)
repetă
    scrie "Introdu parola: "
    citește parola

    dacă parola <> "secret" atunci
        scrie "Parola gresita!"
    sfârșit dacă
până când parola = "secret"

scrie "Acces permis!"
// SOLUȚIE: Transformarea în WHILE
parola ← ""  // Inițializezi cu o valoare care să forțeze intrarea

cât timp parola <> "secret" execută
    scrie "Introdu parola: "
    citește parola

    dacă parola <> "secret" atunci
        scrie "Parola gresita!"
    sfârșit dacă
sfârșit cât timp

scrie "Acces permis!"

9. Când Să Folosești Care? “Arta Alegerei”

FOLOSEȘTE WHILE (cât timp) când:

  1. Poți să NU execuți deloc (condiția falsă de la început)
  2. Citești din fișier până la sfârșit
  3. Aștepți o condiție externă (buton apăsat, mesaj primit)
  4. Numărul de repetări e necunoscut

Exemplu perfect pentru WHILE:

// Verifică conexiunea la internet
while (!esteConectatLaInternet()) {
    cout << "Astept conexiune...\n";
    asteapta(1000);  // Așteaptă 1 secundă
}
cout << "Conectat!";

FOLOSEȘTE REPEAT-UNTIL (do-while) când:

  1. Vrei să execuți cel puțin o dată
  2. Meniu interactiv (“Vrei să continui?”)
  3. Validare input (cere până primești ceva valid)
  4. Simulări/jocuri (“Mai joci o rundă?”)

Exemplu perfect pentru REPEAT-UNTIL:

// Meniu interactiv
do {
    afiseazaMeniu();
    citesteOptiunea();
    executaOptiunea();
} while (optiunea != 'q');  // Până când utilizatorul apasă 'q' pentru quit

FOLOSEȘTE FOR când:

  1. Știi exact de câte ori trebuie să repeți
  2. Parcurgi un vector/matrice (îți știi dimensiunea)
  3. Generezi serii de numere
  4. Simulezi ceva cu un număr fix de iterații

10. PROIECT INTEGRAT: Calculator cu Transformări

#include <iostream>
using namespace std;

int main() {
    int optiune;
    double a, b, rezultat;
    char continuare;

    cout << "=== CALCULATOR CU TRANSFORMARI ===\n\n";

    // Folosim do-while pentru că vrem cel puțin o execuție
    do {
        cout << "\nMeniu operatii:\n";
        cout << "1. Adunare (+)\n";
        cout << "2. Scadere (-)\n";
        cout << "3. Inmultire (*)\n";
        cout << "4. Impartire (/)\n";
        cout << "Alege operatia: ";
        cin >> optiune;

        cout << "Introdu primul numar: ";
        cin >> a;
        cout << "Introdu al doilea numar: ";
        cin >> b;

        // WHILE pentru validare impartire la 0
        while (optiune == 4 && b == 0) {
            cout << "EROARE: Impartire la 0! Introdu alt numar: ";
            cin >> b;
        }

        switch(optiune) {
            case 1: rezultat = a + b; break;
            case 2: rezultat = a - b; break;
            case 3: rezultat = a * b; break;
            case 4: rezultat = a / b; break;
            default: cout << "Optiune invalida!\n"; continue;
        }

        cout << "Rezultat: " << rezultat << endl;

        cout << "\nMai faci un calcul? (d/n): ";
        cin >> continuare;

    } while (continuare == 'd' || continuare == 'D');

    cout << "\nLa revedere!\n";

    // ACUM transformă acest do-while în while!
    // TEMA: Rescrie același program folosind while în loc de do-while
    // Hint: Va trebui să inițializezi 'continuare' cu 'd' 
    // și să modifici condiția while-ului

    return 0;
}

SOLUȚIE transformare do-while → while:

// Versiunea cu WHILE (transformată)
#include <iostream>
using namespace std;

int main() {
    int optiune;
    double a, b, rezultat;
    char continuare = 'd';  // Inițializare forțată pentru prima intrare

    cout << "=== CALCULATOR CU WHILE ===\n\n";

    // Transformarea: do-while → while
    while (continuare == 'd' || continuare == 'D') {
        // ... același cod ca mai sus ...

        cout << "\nMai faci un calcul? (d/n): ";
        cin >> continuare;
    }

    cout << "\nLa revedere!\n";

    return 0;
}

În concluzie, să-ți spun ceva fundamental:
Transformarea structurilor repetitive nu e un truc intelectual gol. E o abilitate practică care îți arată că înțelegi cu adevărat logica din spatele buclelor. Dacă poți transforma un while într-un repeat-until și invers, demonstrezi că înțelegi diferența subtilă dintre “verifică înainte să execuți” și “execută înainte să verifici”.

Dar transformarea mecanică fără înțelegerea logicii poate fi, paradoxal, o sursă de bug-uri subtile dacă nu realizezi că while(x > 0) și repeat ... until(x <= 0) folosesc condiții opuse.

Așa că ai grijă când transformi. Cunoștințe-ti logica: ce vrei să facă bucla? Când vrei să se oprească? Execută mereu cel puțin o dată sau poate să nu se execute deloc? Pentru că puterea de a rescrie un algoritm în formă diferită este goală fără înțelegerea de ce și cum funcționează fiecare formă. Și această putere vine cu o responsabilitate: să fii precis și atent la detalii logice.

Stăpânirea transformărilor între structuri repetitive este o parte integrală a flexibilității în programare. Ai grijă de ea cu aceeași seriozitate.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *