De ce contează?
Gândește-te la o rețetă de prăjitură. Nimeni n-o scrie ca un perete uriaș de text — o împarți în pași cu nume: „prepari aluatul", „faci crema", „coci 40 de minute", „decorezi". Fiecare pas are un nume clar, primește niște ingrediente și scoate ceva gata făcut. Un program e la fel: o problemă mare devine ușoară când o tai în pași cu nume — adică în funcții.
Ideea-cheie
Știi deja cum se scrie o funcție: îi dai un nume, parametri, un corp și o valoare returnată. Întrebarea de acum nu mai e „cum?", ci „când și de ce?". Nu introducem niciun algoritm nou — învățăm cum să tăiem o problemă deja înțeleasă în bucăți cu sens.
O problemă rezolvabilă e aproape mereu o înșiruire de subsarcini: citești datele, le verifici, calculezi ceva, afișezi rezultatul. Fiecare subsarcină are un început și un sfârșit clar — și de cele mai multe ori un nume firesc. Acel nume firesc este semnalul că ai în față o funcție.
O funcție bună e o singură subsarcină cu un nume clar, un input și un output. Întreabă-te: „pot să dau un nume scurt acestui bloc de cod?". Dacă da, e candidat de funcție. Două semne și mai puternice că ai nevoie de o funcție: cod care se repetă (același bloc apare de două-trei ori) sau un bloc pe care l-ai putea explica cuiva într-o propoziție („aici verific dacă numărul e prim").
Cum alegi
Hai să luăm o problemă mică și concretă: „câte numere prime sunt într-un vector?". Nu e nimic greu aici — dar tocmai pe ceva simplu vezi cel mai clar cum se face tăietura.
Citește problema cu voce tare și subliniază verbele: citești vectorul, pentru fiecare element verifici dacă e prim, numeri câte sunt și afișezi răspunsul. Patru verbe, patru subsarcini. Le punem pe fiecare la proba „are nume? are input? are output?":
| Subsarcină | Nume firesc | Input | Output | Merită funcție? |
|---|---|---|---|---|
| citești datele | citeste | nimic (sau adresa vectorului) | vectorul + n | da, e un bloc cu sens |
| testezi un număr | estePrim | un număr x | adevărat / fals | da — și se repetă pentru fiecare element |
| numeri primele | numara | vectorul + n | un întreg | da, ascunde bucla |
| afișezi | direct în main | un întreg | — | nu, e o singură linie |
Vezi logica deciziei? estePrim câștigă instant: e o întrebare clară („e prim
sau nu?") și o folosești de n ori — exact cazul „cod care se repetă".
citeste și numara câștigă pentru că au fiecare un nume firesc și ascund o
buclă întreagă în spatele lui. Afișarea, în schimb, e o singură linie cu un nume
vag („afisez") — n-are rost s-o scoți; ar fi mai mult zgomot decât folos.
Iată scheletul, fără să implementăm nimic complicat — observă doar cât de mult
seamănă main cu enunțul citit cu voce tare:
#include <iostream>
using namespace std;
// testeaza o singura subsarcina: e x prim?
bool estePrim(int x) {
if (x < 2) return false;
for (int d = 2; d * d <= x; d++)
if (x % d == 0) return false;
return true;
}
// citeste vectorul; intoarce cate numere a citit
int citeste(int v[]) {
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> v[i];
return n;
}
// numara primele folosind estePrim
int numara(int v[], int n) {
int cate = 0;
for (int i = 0; i < n; i++)
if (estePrim(v[i])) // refolosim subsarcina
cate++;
return cate;
}
int main() {
int v[1000];
int n = citeste(v);
cout << numara(v, n); // citeste ca enuntul: numara primele
return 0;
}main a devenit un mic rezumat al problemei: citește, numără, afișează. Toată
„mecanica" (cum testezi un prim, cum parcurgi vectorul) stă în funcții, fiecare
cu un singur lucru de făcut. Asta e tot rostul descompunerii: când recitești
codul peste o săptămână, înțelegi ce face din nume, fără să recitești cum.
Reține criteriul, nu lista: scoți un bloc în funcție când are un nume firesc,
când se repetă, sau când ascunde detalii pe care main n-are nevoie să le vadă.
Îl lași pe loc când e o singură linie folosită o singură dată.
Patru capcane care strică o descompunere bună:
- Funcția care face tot. O funcție
rezolva()de 60 de linii care citește, calculează și afișează nu e descompunere — e tot peretele de cod, doar mutat. O funcție = o subsarcină. - Prea mulți parametri sau dependență de globale. Dacă o funcție cere șapte parametri, probabil amesteci mai multe subsarcini; taie-le. Iar dacă „merge" doar pentru că citește variabile globale, n-are de fapt un input clar — devine imposibil de refolosit și de testat.
- Descompunere prematură. Nu scoate în funcție un bloc pe care îl folosești o singură dată și care oricum e o linie. Funcțiile au sens pentru ce se repetă sau pentru ce merită un nume — nu pentru a împărți de dragul împărțirii.
- Nume vagi.
f,proceseaza,calculnu spun nimic. Numele bun spune ce face, nu cum:estePrim,citeste,numara. Dacă nu găsești un nume scurt și clar, probabil blocul nu e încă o subsarcină curată.