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:
- Pornesc diferit
- Verifică condiția în momente diferite
- 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 5repeat ... 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:
- Inversează condiția (neg-o!)
- Asigură-te că instrucțiunile se execută cel puțin o dată
- Pune bucla într-un
ifdacă 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:
- Scrii instrucțiunile o dată înainte de while
- Pui aceleași instrucțiuni în while
- 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:
- Poți să NU execuți deloc (condiția falsă de la început)
- Citești din fișier până la sfârșit
- Aștepți o condiție externă (buton apăsat, mesaj primit)
- 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:
- Vrei să execuți cel puțin o dată
- Meniu interactiv (“Vrei să continui?”)
- Validare input (cere până primești ceva valid)
- 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:
- Știi exact de câte ori trebuie să repeți
- Parcurgi un vector/matrice (îți știi dimensiunea)
- Generezi serii de numere
- 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.
Leave a Reply