Clasa a IX-a — Capitol 3.1 Subprograme

Transmiterea parametrilor

Progres lectie:
0%
🎯

Obiectivul lectiei

Vei intelege exact ce se intampla cu datele cand apelezi o functie: de ce uneori modificarile din functie nu se propaga in exterior, si cum poti controla acest comportament in Python si C++.

Dupa aceasta lectie vei putea:

  • Sa explici diferenta dintre transmiterea prin valoare si prin referinta
  • Sa identifici variabilele locale si globale si domeniul lor de vizibilitate
  • Sa scrii functii Python care modifica date prin returnare sau prin obiecte mutabile
  • Sa folosesti & in C++ pentru parametri prin referinta (EXCLUSIV intensiv)
  • Sa eviti capcanele clasice: swap care nu functioneaza, efecte laterale neasteptate
  • Sa analizezi complexitatea O(1) vs O(n) la transmiterea prin valoare vs referinta

Incearca singur!

Provocare initiala:

Ce va afisa acest cod Python? Gandeste-te inainte sa rulezi:

def modifica(x):
    x = x + 100

val = 5
modifica(val)
print(val)
💡 Ai nevoie de un indiciu?

In Python, numerele sunt imutabile. Parametrul x este o copie locala. x = x + 100 creeaza un nou obiect, nu modifica originalul.

1

1. Transmiterea prin valoare

Cand transmiti un argument prin valoare, functia primeste o copie a datei originale. Modificarile in interiorul functiei afecteaza doar copia — originalul ramane neschimbat.
Analogie:
E ca si cum dai cuiva o fotografie a unui document. Daca ei scriu pe fotografie, originalul tau ramane intact.
Exemplu Python — valoare imutabila (int):
def dubleaza(x):
    x = x * 2
    print(f"  In functie, x = {x}")

valoare = 5
print(f"Inainte de apel: valoare = {valoare}")
dubleaza(valoare)
print(f"Dupa apel: valoare = {valoare}")
Output real (rulat cu python):
Inainte de apel: valoare = 5
  In functie, x = 10
Dupa apel: valoare = 5
🔎Ce se intampla in memorie
dubleaza(valoare) leaga parametrul x la obiectul 5. Instructiunea x = x * 2 creeaza un nou obiect 10 si leaga x de el. valoare inca pointeaza spre 5. Complexitate copiere: O(1) pentru tipuri primitive.
2

2. Obiecte mutabile — efect de referinta implicit

In Python, tipurile mutabile (list, dict, set) transmise ca parametri impart acelasi obiect cu apelatorul. Modificarile in-place (append, del, actualizare cheie) sunt vizibile in afara functiei.
⚠ Distinctie importanta

Tehnic Python transmite referinta prin valoare. Daca faci lst = [altceva] in functie (reasignare), originalul NU se schimba. Doar metodele care modifica obiectul in-place au efect exterior.

Exemplu Python — lista (obiect mutabil):
def adauga_element(lst, val):
    lst.append(val)  # modifica obiectul in-place
    print(f"  In functie, lst = {lst}")

numere = [1, 2, 3]
print(f"Inainte de apel: numere = {numere}")
adauga_element(numere, 99)
print(f"Dupa apel: numere = {numere}")
Output real (rulat cu python):
Inainte de apel: numere = [1, 2, 3]
  In functie, lst = [1, 2, 3, 99]
Dupa apel: numere = [1, 2, 3, 99]
Regula rapida Python:
  • int, float, str, tuple → imutabile → functia nu poate modifica originalul prin reasignare
  • list, dict, set → mutabile → metodele in-place modifica originalul
3

3. Variabile locale si globale

  • Variabila locala: definita in interiorul unei functii; vizibila si activa doar pe durata executiei acelei functii. La finalul functiei, este distrusa.
  • Variabila globala: definita in afara oricarei functii; vizibila din toata aplicatia. In Python, ca sa o modifici dintr-o functie, trebuie sa declari global numeVariabila.
Exemplu Python — local vs global:
contor_global = 0  # variabila globala

def incrementeaza():
    global contor_global        # fara aceasta linie: UnboundLocalError
    contor_global += 1
    variabila_locala = 42    # exista doar in aceasta functie
    print(f"  contor_global={contor_global}, variabila_locala={variabila_locala}")

print(f"Inainte: contor_global = {contor_global}")
incrementeaza()
incrementeaza()
print(f"Dupa 2 apeluri: contor_global = {contor_global}")

try:
    print(variabila_locala)
except NameError:
    print("variabila_locala nu exista in afara functiei")
Output real (rulat cu python):
Inainte: contor_global = 0
  contor_global=1, variabila_locala=42
  contor_global=2, variabila_locala=42
Dupa 2 apeluri: contor_global = 2
variabila_locala nu exista in afara functiei
🚫 Buna practica: evita variabilele globale

Variabilele globale fac codul greu de depanat. Solutia corecta: transmite date prin parametri si returneaza rezultate. Acceptabil doar pentru constante (MAX = 1000).

4

4. Capcana swap-ului — solutii corecte in Python

O greseala frecventa: incerci sa interschimbi doua variabile printr-o functie care le primeste ca parametri simpli (int). Nu functioneaza — functia primeste copii, nu referinte.
Demonstratie — swap gresit vs corect:
def swap_gresit(a, b):
    temp = a
    a = b
    b = temp
    print(f"  In functie: a={a}, b={b}")

x, y = 10, 20
print(f"Inainte: x={x}, y={y}")
swap_gresit(x, y)
print(f"Dupa swap_gresit: x={x}, y={y}  <-- nu s-a schimbat!")

# Swap corect: returnare multipla
def swap_corect(a, b):
    return b, a

x, y = swap_corect(x, y)
print(f"Dupa swap_corect: x={x}, y={y}  <-- corect!")
Output real (rulat cu python):
Inainte: x=10, y=20
  In functie: a=20, b=10
Dupa swap_gresit: x=10, y=20  <-- nu s-a schimbat!
Dupa swap_corect: x=20, y=10  <-- corect!
Cel mai Pythonic swap — O(1)
x, y = y, x  # Python evalueaza dreapta integral, APOI asigneaza
Python creeaza un tuplu temporar (y, x) si il dezimpacheteaza. Curat, O(1), fara variabila temporara explicita.
5

5. C++: prin valoare vs prin referinta EXCLUSIV INTENSIV

⚡ Sectiune doar pentru intensiv informatica

In C++ ai control explicit: int x = prin valoare (copie), int& x = prin referinta (alias al originalului). Referinta e mai eficienta pentru obiecte mari si necesara cand vrei sa modifici originalul.

C++ — valoare vs referinta:
#include <iostream>
using namespace std;

// Prin valoare: primeste COPIA lui x
void dubleaza_val(int x) {
    x = x * 2;
    cout << "  In functie (val): x = " << x << endl;
}

// Prin referinta: x este alias al argumentului original
void dubleaza_ref(int& x) {
    x = x * 2;
    cout << "  In functie (ref): x = " << x << endl;
}

int main() {
    int a = 5;
    cout << "Inainte (val): a = " << a << endl;
    dubleaza_val(a);
    cout << "Dupa (val): a = " << a << " (nemodificat)" << endl;
    int b = 5;
    cout << "Inainte (ref): b = " << b << endl;
    dubleaza_ref(b);
    cout << "Dupa (ref): b = " << b << " (modificat!)" << endl;
    return 0;
}
Output real (compilat g++ -std=c++17, apoi rulat):
Inainte (val): a = 5
  In functie (val): x = 10
Dupa (val): a = 5 (nemodificat)
Inainte (ref): b = 5
  In functie (ref): x = 10
Dupa (ref): b = 10 (modificat!)
📈Complexitate: de ce conteaza referinta la structuri mari
  • void f(int x) → copie: O(1)
  • void f(vector<int> v) → copie: O(n) (copiaza tot vectorul)
  • void f(const vector<int>& v) → referinta const: O(1) (transmite doar adresa)
const T& = pattern standard C++ pentru argumente read-only de tip complex.
6

6. C++: globale, locale si swap prin referinta EXCLUSIV INTENSIV

⚡ Sectiune doar pentru intensiv informatica

Swap-ul prin referinta este exemplul clasic al necesitatii parametrilor prin referinta in C++. Fara referinte, swap-ul nu functioneaza pe variabile int.

C++ — globale, locale, swap prin referinta:
#include <iostream>
using namespace std;

int global_contor = 0;  // variabila globala

void incrementeaza() {
    global_contor++;
    int local_val = 42;  // locala: distrusa la iesire
    cout << "  global_contor=" << global_contor
         << ", local_val=" << local_val << endl;
}

void swap_ref(int& a, int& b) {
    int temp = a; a = b; b = temp;
}

int main() {
    cout << "=== Globale vs Locale ===" << endl;
    cout << "Inainte: global_contor = " << global_contor << endl;
    incrementeaza(); incrementeaza();
    cout << "Dupa 2 apeluri: global_contor = " << global_contor << endl;
    cout << endl << "=== Swap prin referinta ===" << endl;
    int x = 10, y = 20;
    cout << "Inainte: x=" << x << ", y=" << y << endl;
    swap_ref(x, y);
    cout << "Dupa: x=" << x << ", y=" << y << " (modificat!)" << endl;
    return 0;
}
Output real (compilat g++ -std=c++17, apoi rulat):
=== Globale vs Locale ===
Inainte: global_contor = 0
  global_contor=1, local_val=42
  global_contor=2, local_val=42
Dupa 2 apeluri: global_contor = 2

=== Swap prin referinta ===
Inainte: x=10, y=20
Dupa: x=20, y=10 (modificat!)
Rezumat comparativ Python vs C++:
AspectPythonC++ (intensiv)
Int prin valoareimplicit (imutabil)void f(int x)
Modificare variabilareturnare + reasignarevoid f(int& x)
Variabila globalaglobal x obligatoriuacces direct (nivel fisier)
Swap corecta, b = b, a sau returnswap_ref(int&, int&)
Obiect mare read-onlyimutabil sau copieconst T& O(1)

Exercitii practice

Exercitiul 1 (Nivel minim) — Analizeaza codul

Ce va afisa urmatorul cod? Urmareste pas cu pas ce se intampla cu variabilele:

def modifica(x, lst):
    x = x + 10
    lst.append(x)

a = 5
b = [1, 2]
modifica(a, b)
print(a, b)

Explica: de ce a nu se modifica, dar b se modifica?

Exercitiul 2 (Nivel standard) — Scrie functia corecta

Scrie o functie Python calculeaza_medie(note) care primeste o lista de note (int) si returneaza media (float). Functia nu trebuie sa modifice lista originala. Dupa apel, verifica ca lista nu s-a schimbat.

Bonus: ce complexitate O(?) are functia ta?

Exercitiul 3 (Nivel performanta) — C++ referinta EXCLUSIV INTENSIV

Scrie in C++ o functie void normalizeaza(vector<int>& v) care imparte fiecare element al vectorului la maximul sau (in-place, fara vector nou). Justifica de ce ai folosit & si care ar fi complexitatea daca ai transmis vectorul prin valoare. Adauga un guard pentru vectorul gol.

Ce ai invatat astazi

  • Transmiterea prin valoare: functia primeste o copie, originalul ramane neschimbat
  • In Python: int/str/tuple = imutabile (comportament valoare), list/dict = mutabile (efect referinta prin metode in-place)
  • Variabile locale: exista doar in corpul functiei, distruse la iesire
  • Variabile globale: vizibile global, modificabile in Python cu global
  • Swap corect in Python: a, b = b, a sau returnare multipla
  • C++ (intensiv): int& x = referinta explicita; const T& = O(1) fara risc de modificare

Urmatoarea lectie

Continua cu Introducere OOP: clase, obiecte, atribute si metode.

Continua →