InfoBook.ro ← Harta anului
Modul 05 · Subprograme
Modul 05 · Conținut 3.1

Cod scris o dată, folosit oricând

Subprogramele (funcțiile) sunt „rețetele" programării: le scrii o singură dată, le apelezi de câte ori vrei, cu ingrediente diferite. De aici începe programarea modulară — și jumătate din subiectul III de BAC.

🎯 Obiectiv 1Cunoști cele 4 componente: antet, parametri, corp, valoare de întoarcere.
🎯 Obiectiv 2Alegi corect între void (acțiune) și return (rezultat) și înțelegi parametrii formali vs efectivi.
🎯 Obiectiv 3Stăpânești valoare vs referință (&), scope-ul variabilelor și spargi o problemă în subprograme (proiectare modulară).
1

Ce e un subprogram — analogia „rețetei"

O colecție de instrucțiuni care îndeplinesc o sarcină, grupate sub un nume. Ca o rețetă de prăjitură: o scrii o dată, o folosești mereu — cu ingrediente diferite.

1️⃣ Antetul

Numele funcției + lista de parametri. Cartea de vizită.

2️⃣ Parametrii

„Ingredientele" pe care le primește la fiecare apel.

3️⃣ Corpul

Instrucțiunile care se execută efectiv.

4️⃣ Valoarea de întoarcere

Rezultatul trimis înapoi. Opțional — poate lipsi (void).

▶ Primul tău apel de funcție: dublu(5), pas cu pas

▸ programul principal — main()

▸ funcția — dublu()

 
Variabile
Ecran
 
2

Două tipuri: void vs return

Procedural (void) = execută o acțiune (afișează), fără să întoarcă nimic. Operand (return) = calculează și returnează o valoare pe care o poți folosi mai departe.

▶ Funcție void: stelute(3) — acțiune, fără rezultat

▸ programul principal

▸ funcția — stelute()

 
Variabile
Ecran
 

▶ Funcție cu return: maxim(7, 3) — rezultatul se întoarce în main

▸ programul principal

▸ funcția — maxim()

 
Variabile
Ecran
 
Cod mort: orice instrucțiune scrisă după return nu se execută niciodată. Când void? Când vrei să afișezi / desenezi / modifici. Când return? Când ai nevoie de rezultat: rez = calcul(n), if (estePrim(n)).
3

Parametri formali vs efectivi

Formali = numele din definiția funcției. Efectivi = valorile trimise la apel. La apel, efectivii se copiază în formali, în ordinea pozițiilor.

▶ cmmdc(12, 8) — Euclid devine funcție reutilizabilă

▸ programul principal

▸ funcția — cmmdc()

 
Variabile
Ecran
 
Observă la primul pas al apelului: 12 (efectiv) se copiază în a (formal), 8 în b — strict după poziție. Numele din main (n1, n2) și numele din funcție (a, b) pot fi complet diferite: funcția lucrează pe propriile ei copii.
4

Transmiterea: prin valoare vs prin referință

Același apel dubleaza(n) — două efecte total diferite. Privește memoria în paralel.

Prin VALOARE — copie

1void dubleaza(int x) {
2 x = x * 2; // modifică COPIA
3}
4int n = 5;
5dubleaza(n);
6cout << n; // tot 5!
main: n
5
funcție: x
—

🐍 Python — la fel, tot pe copie:

1def dubleaza(x):
2 x = x * 2 # modifică copia locală
3n = 5
4dubleaza(n)
5print(n) # tot 5!

Prin REFERINȚĂ (&) — original

1void dubleaza(int &x) {
2 x = x * 2; // modifică ORIGINALUL
3}
4int n = 5;
5dubleaza(n);
6cout << n; // 10! MODIFICAT
main: n
5
funcție: x ≡ n
5

🐍 Python — NU are & pentru numere; primești prin return:

1def dubleaza(x):
2 return x * 2
3n = 5
4n = dubleaza(n) # n primește 10
5print(n) # 10! „modificat"
Apasă „Pas" și compară cele două lumi la fiecare moment.

Exemplul clasic — swap (interschimbarea)

1void interschimba(int &a, int &b) {
2 int aux = a;
3 a = b;
4 b = aux;
5}
6int x = 3, y = 7;
7interschimba(x, y);
8cout << x << " " << y; // 7 3
1# Python — nu ai nevoie de funcție:
2x, y = 3, 7
3x, y = y, x # swap direct!
4 
5# dacă totuși vrei funcție:
6def swap(a, b):
7 return b, a
8x, y = swap(x, y)

🐍 De ce Python n-are & — două cuvinte de lămurit: „scalar" și „imutabil"

„Scalar" = o valoare SINGURĂ
Un număr, un caracter, un adevărat/fals — o singură informație, nu o colecție.
int, float, bool → scalari
list → NU e scalar: e o colecție de valori
„Imutabil" = nu poate fi schimbat pe loc
Odată creată, valoarea rămâne așa. Când scrii x = x + 1, Python nu „modifică" pe 5 — face un obiect nou, 6, și leagă numele x de el.
Mutabil = se poate schimba pe loc (lista!).

Consecința pentru funcții — aceeași regulă, două rezultate diferite:

✔ SCALAR (imutabil) — originalul e în siguranță

1def f(x):
2 x = x * 2 # leagă x local de o valoare NOUĂ
3n = 5
4f(n)
5print(n) # 5 — NEschimbat

⚠ LISTĂ (mutabilă) — originalul SE modifică

1def f(lst):
2 lst[0] = 99 # schimbă CHIAR lista primită
3v = [1, 2, 3]
4f(v)
5print(v) # [99, 2, 3] — MODIFICAT!
Cum ții minte: funcția primește mereu o etichetă lipită pe același obiect. La un scalar nu poți schimba obiectul (e imutabil), așa că orice atribuire mută doar eticheta locală → originalul scapă neatins. La o listă poți schimba conținutul obiectului (e mutabilă), iar afară se vede, pentru că e același obiect.
Capcana finală: def f(lst): lst = [7,7,7] NU modifică originalul — aici doar muți eticheta, nu schimbi conținutul! Ca să modifici lista, o schimbi pe elemente (lst[0] = …, lst.append(…)).
5

Variabile locale vs globale (scope)

Locală = trăiește doar cât rulează funcția. Globală = vizibilă peste tot. Cine „vede" ce?

⚙ C++

1int x = 10; // GLOBALĂ
2void schimba() {
3 int x = 99; // LOCALĂ!
4 cout << x; // 99
5}
6schimba();
7cout << x; // tot 10!

🐍 Python — aceeași regulă:

1x = 10 # GLOBALĂ
2def schimba():
3 x = 99 # LOCALĂ — altă variabilă!
4 print(x) # 99
5schimba()
6print(x) # tot 10!
7# global x ar modifica-o — de evitat!
🌍 ZONA GLOBALĂ
x = 10
📦 ZONA LOCALĂ — schimba()
x = 99

↑ ascunde x-ul global cât timp funcția rulează

Alege o perspectivă — vezi ce variabile sunt vizibile din fiecare zonă.
Regula de aur: folosește cât mai puține variabile globale. Trimite datele prin parametri și primește-le prin return — programele devin ușor de testat și fără efecte-surpriză.
6

Proiectarea modulară — cum spargi o problemă în subprograme

Până acum ai învățat cum se scrie un subprogram. Acum înveți când și ce să faci subprogram: să iei o problemă mare și s-o tai în bucăți cu o singură treabă fiecare.

🧩 Problema: „Se citesc n numere. Câte sunt prime? Care e suma cifrelor celui mai mare?"

Scrisă „la grămadă", într-un singur main, iese o funcție lungă cu bucle imbricate greu de urmărit. Întrebarea corectă: ce sarcini mici se repetă sau se pot testa separat?

🔍 estePrim(x)

Primește: un număr
Întoarce: adevărat/fals
Treaba lui: doar să decidă dacă e prim — nimic altceva.

➕ sumaCifre(x)

Primește: un număr
Întoarce: suma cifrelor
Treaba lui: doar atât — îl reutilizezi oriunde.

🎬 main()

Treaba lui: citește datele, apelează funcțiile și afișează. Nu calculează el nimic complicat — coordonează.

Test rapid: pentru intrarea 6 / 7 12 5 49 23 100 → prime: 3 (7, 5, 23) și suma cifrelor maximului (100): 1.

📐 Cele 4 întrebări înainte să scrii o funcție

1. Ce treabă are? — o singură propoziție. Dacă ai nevoie de „și", probabil sunt două funcții.
2. Ce primește? — parametrii. Cât mai puțini, doar ce-i trebuie.
3. Ce dă înapoi? — o valoare (return) sau doar o acțiune (void)?
4. Cum o testez singură? — dacă o poți verifica pe un exemplu mic, e bine gândită.
Semne că trebuie să spargi codul: aceeași secvență apare de două ori (⇒ fă-o funcție) · o funcție nu încape pe un ecran · nu poți explica ce face într-o singură frază · trebuie să comentezi „aici începe partea care…" (⇒ acea parte e o funcție).
7

Exerciții rezolvate — patru șabloane de aur

Modele pe care le vei rescrie de zeci de ori. Observă: fiecare problemă = o funcție cu treabă clară.

1 · return — numărul perfect

Un număr e perfect dacă suma divizorilor săi proprii îl dă chiar pe el: 6 = 1+2+3, 28 = 1+2+4+7+14.

Test: 6 → DA · 28 → DA · 12 → NU · 496 → DA. Folosește trucul perechilor de divizori (√n) din Modulul 03.

2 · return — oglindit, apoi palindrom

Cel mai elegant tipar: o funcție care apelează altă funcție. Scrii oglindit o dată și palindromul devine o singură linie.

Test: 121 → palindrom · 573 → 375 · 1221 → palindrom. Ideea de aur: funcțiile mici se combină în funcții mari.

3 · void — desenează un chenar

Funcție void cu trei parametri (lățime, înălțime, caracter): desenează doar conturul, interiorul rămâne gol.

›chenar(7, 4, '*') afișează:
 *******
 * *
 * *
 *******

void = doar afișează, nu întoarce nimic. Schimbi parametrii → alt desen, fără să rescrii codul.

4 · referință — DOUĂ rezultate deodată

Cu return poți întoarce o singură valoare. Dar împărțirea dă două: câtul și restul. Soluția în C++: parametri prin referință.

Fără &, funcția ar modifica doar copiile și în main c și r ar rămâne nedefinite. Ăsta e exact subiectul categoriei „subprograme care întorc valori prin parametri".

8

Testarea subprogramelor — cazuri limită și bug-uri clasice

Un subprogram care merge pe exemplul din enunț poate fi tot greșit. Înainte să-l declari gata, testează-l pe cazurile limită: zero, valori negative, listă goală, un singur element.

🐞 Vânătoarea de bug-uri — găsești greșeala înainte să apeși?

Fiecare cod de mai jos pare corect și chiar funcționează pe exemplele obișnuite. Citește-l, răspunde la întrebarea de sub el și discutați în clasă — abia apoi se deschide explicația.

🔍 Bug 1 — funcția „semn": merge pe 5 și pe −3, dar…

1int semn(int x) {
2 if (x > 0) return 1;
3 if (x < 0) return -1;
4}

Întrebare: ce se întâmplă dacă x este exact 0?

🔍 Bug 2 — media unei liste: corectă… până când nu mai e

1def medie(v):
2 return sum(v) / len(v)

Întrebare: ce se întâmplă dacă lista v este goală?

🔍 Bug 3 — funcția dublează, dar în main nu se schimbă nimic

1void dubleaza(int x) {
2 x = x * 2;
3}
4int n = 5;
5dubleaza(n);
6cout << n;

Întrebare: ce afișează — 10 sau 5? Și de ce?

🔍 Bug 4 — mesajul care nu apare niciodată

1int suma(int a, int b) {
2 return a + b;
3 cout << "am calculat suma";
4}

Întrebare: de ce nu se afișează niciodată mesajul de la linia 3?

✅ Lista de verificare — 5 teste înainte să spui „gata"

1. Cazul obișnuit — exemplul din enunț. Dă rezultatul așteptat?
2. Zero — ce face funcția pentru 0? (împărțiri, semn, lungime)
3. Valori negative — merge și pentru -7? (la cifre, la radical!)
4. Un singur element / listă goală — nu crapă?
5. Toate drumurile returnează? — urmărește fiecare if și verifică dacă are unde ajunge la un return.
Sfat de BAC: la subiectul III ți se cere un subprogram cu antet impus. Înainte să-l predai, rulează-l mental pe trei valori: una obișnuită, una la limită (0 sau 1) și una negativă. Majoritatea punctelor pierdute vin din cazurile limită, nu din algoritm.
9

Test interactiv — subprograme

10 întrebări, exact conceptele de mai sus. Țintește 10/10.

10

Fișa de sinteză

Programarea modulară pe scurt.

Subprogram = instrucțiuni cu nume: antet + parametri + corp + valoare de întoarcere.
void = acțiune fără rezultat; return = rezultat folosibil în expresii. După return: cod mort.
Formali (din definiție) vs efectivi (de la apel) — corespondență strict pe poziții.
Prin valoare = copie, originalul intact; prin referință (&) = modifici originalul (swap!).
Python: valorile simple (int, float, bool) sunt imutabile → funcția nu poate schimba originalul; listele sunt mutabile → lst[0]=99 se vede și afară.
Locală trăiește cât funcția; globală peste tot — dar minimizeaz-o: parametri + return.
Proiectare modulară: sparge problema în funcții mici, fiecare cu o singură treabă clară.
Testare: verifică mereu cazurile limită — 0, valori negative, listă goală — și că toate drumurile din funcție ajung la un return.
La BAC (subiectul III): ți se cere exact asta — un subprogram cu antet impus. Exersează antetele!
Intră și lucrează pe pbinfo.ro — cele două categorii care acoperă exact acest modul. Scrie fiecare soluție ca funcție separată:
📚 Subprograme care returnează valori (return) ↗ 📚 Subprograme care întorc valori prin parametri (&) ↗
← anteriorModul 04a · Clasa list (Python) urmează →Modul 06 · Funcții & biblioteci