Sunteți pe pagina 1din 37

Limbaje de programare

Cuprins
Apeluri de funcții
Recursivitate
Recursivitate vs iterație
Bibliografie selectivă
Apeluri de funcții
Ce face o funcție?
◦ Calculează o valoare
int suma(int a, int b) {
return a+b; }
◦ Produce un efect (ex. afișează un mesaj)
void eroare(int cod) {// tipul void: nu returnează nimic
printf("Eroare cu codul %d\n", cod); }
◦ Efect + valoare (scrie și calculează)
int sqr(int x){
printf("Calculam patratul lui %d\n", x);
return x * x; }
Apeluri de funcții
Să ne amintim! #include <stdio.h>
int f(int x) //definirea funcției f
Pentru ca o funcție să producă un efect
{
sau să calculeze o valoare, ea trebuie
să fie apelată. return x*x;
}
Exemplu de apel: int main()
{
int z, y;
printf("Dati valoarea numarului : ");
scanf("%d", &z);
y = f(z); //apelul funcției f
printf("%d la patrat = %d\n",z,y); //afisarea rezultatului
return 0;
}
Apeluri de funcții
Declararea unei funcții:
int suma(int a, int b); //tip returnat, nume funcție, tip și nume parametri

Definirea unei funcții:


int suma(int a, int b)
{
return a+b; //corpul funcției între {}
}
Apeluri de funcții
Apelul unei funcții:
int s, x=-5,y=8;
s=suma(5,8); //o funcție poate fi apelată cu valori constante
s=suma(x,y); //sau cu variabile
s=suma(x*2,y); //sau cu expresii
s=suma(abs(x),y); //sau cu rezultatul altor funcții

Atenție:
Nu apelăm funcții cu variabile neinițializate!
Apeluri de funcții
Să ne amintim:
Orice program are o funcție main
int main(void)
{ // --> AICI <-- scriem ce sa faca programul
return 0;
}
Execuția programului începe cu funcția main.
De aici apelăm celelalte funcții (scrise înainte).
Apeluri de funcții
Observație: Codul se scrie doar în funcții
tip functie1 ( parametri )
{ // codul functiei 1 }
// NU scriem cod între funcții
tip functie2 ( parametri )
{ // codul functiei 2 }
// NU scriem cod aici
int main(void)
{ // apelează funcție1, funcție2
return 0;
// NU scriem cod după return }
Apeluri de funcții
Exemplul alăturat calculează X6=(x*x2)2 #include <stdio.h>
int sqr(int x)
Rezultatul execuției programului este: {
printf("Patratul lui %d e %d\n", x, x*x);
return x * x;
}
int main(void)
{
printf("2 la a 6-a e %d\n", sqr(2 * sqr(2)));
return 0;
}
Apeluri de funcții
Transmiterea parametrilor la funcții:
◦ se evaluează (calculează valoarea) pentru toate argumentele funcției
◦ valorile se atribuie la parametrii formali (numele din definiția funcției) apoi se începe execuția
funcției cu aceste valori
◦ ca orice program în C, programul anterior începe cu execuția funcției main (în cazul anterior
cu apelul la printf );
◦ funcția printf are nevoie de valoarea argumentelor sale.
◦ valoarea primului argument se știe (o constantă șir) pentru al doilea argument trebuie apelat:
sqr(2 * sqr(2)) la rândul lui, sqr exterior are nevoie de valoarea argumentului 2 * sqr(2),
pentru care trebuie apelat întâi sqr(2) ⇒ ordinea apelurilor: sqr(2), apoi sqr(8), apoi printf din
main
Atenție: Funcția NU începe execuția fără să aibă toate argumentele calculate!
Apeluri de funcții
Funcțiile pot avea sau nu parametri și rezultat
int f(int x)
{
return (x + 2) % 26;
}
Folosim rezultatul:
◦ îl atribuim: int y = f(3);
◦ îl tipărim: printf("%d\n", f(25));
◦ într-o expresie: a=b+f(83); (inclusiv în apelul la altă funcție)
Apeluri de funcții void afisare_n(int n)
{ //functie fara rezultat
Putem scrie funcții fără rezultat ⇒ tipul returnat e void int i;
for (i=0;i<n;i++)
Folosim funcția într-o instrucțiune: printf(“%d “,i);
afișare_n(16); }

Funcții fără parametric - definim cu void în locul listei parametrilor int citeste_n(void)
{ //functie fara parametri
Apelăm întotdeauna cu paranteze: int n;
int nr = citeste_n(); scanf(“%d”, &n);
afisare_n(5); return n;
}
Recursivitate
În matematică am întâlnit șiruri recurente ca de exemplu:
◦ progresie aritmetică:
𝑥0 = 𝑏
𝑥𝑛 = 𝑥𝑛−1 + 𝑟, 𝑝𝑒𝑛𝑡𝑟𝑢 𝑛 > 0
Exemplu: 1,4,7,10,13,... (b = 1, r = 3)
◦ progresie geometrică:
𝑥0 = 𝑏
𝑥𝑛 = 𝑥𝑛−1 ∗ 𝑟, 𝑝𝑒𝑛𝑡𝑟𝑢 𝑛 > 0
Exemplu: 3,6,12,24,48,... (b = 3, r = 2)
Nu se calculează xn direct, ci din aproape în aproape, folosind xn−1.
O noțiune este recursivă dacă este folosită în propria sa definiție.
Recursivitate
Recursivitatea este importantă în informatică pentru că reduce o problemă la un
caz mai simplu al aceleiași probleme
(un singur element)
obiecte: un șir este
un element urmat de un șir

ex. cuvânt (șir de litere); număr (șir de cifre zecimale)


Recursivitate
Care sunt elementele unei definiții recursive?
1. Cazul de bază (NU necesită apel recursiv) = cel mai simplu caz pentru definiția (noțiunea)
dată, definit direct termenul inițial dintr-un șir recurent: x0 un element, în definiția: șir =
element sau șir + element
Este o EROARE dacă lipsește cazul de bază (=> apel recursiv infinit!)
2. Relația de recurență propriu-zisă – definește noțiunea, folosind un caz mai simplu al aceleiași
noțiuni
3. Demonstrație de oprire a recursivității după un număr finit de pași (ex. o mărime nenegativă
care descrește când aplicăm definiția) – la șiruri recurente: indicele (≥ 0 dar mai mic în corpul
definiției) – la obiecte: dimensiunea (definim obiectul prin alt obiect mai mic)
Recursivitate
Sunt corecte următoarele definiții?
? xn+1 = 2·xn
? xn = xn+1 −3
? an = a·a·...·a (de n ori)
? o frază este o înșiruire de cuvinte
? un șir e un șir mai mic urmat de un alt șir mai mic
? un șir e un caracter urmat de un șir
O definiție recursivă trebuie să fie bine formată (vezi condițiile 1-3); ceva nu se poate defini doar
în funcție de sine însuși; se pot utiliza doar noțiuni deja definite; nu se poate genera un calcul
infinit (trebuie să se oprească)
Recursivitate
Definiție: O functie se numeste recursiva, daca ea se autoapeleaza direct sau
indirect.
Recursivitatea poate fi:
◦ directa – o funcție F conține o referință la ea insăși,
sau
◦ indirecta – o funcție F conține o referință la o funcție G ce include o referință la F.
Recursivitate
Implementare:
Cazul de bază reprezintă condiția de oprire și trebuie să nu lipsească pentru a evita un număr
infinit de apeluri.
Condiţia de oprire trebuie să fie una generică, şi să oprească recurenţa în orice situaţie.
Această condiţie se referă în general la parametrii de intrare, pentru care la un anumit moment,
răspunsul poate fi returnat direct, fără a mai fi necesar un apel recursiv suplimentar.
Apelul recursiv poate fi direct sau indirect.
#include <stdio.h>

Recursivitate double pow(double x, int n)


{
if (n == 0) //conditia de oprire
Funcția putere: //atentie functia se blocheaza pentru n<0
return 1;
R->R //atenție fiecare funcție recursiva trebuie să aiba o conditie de oprire
return x*pow(x, n - 1);
1, 𝑛 = 0 }
𝑥𝑛 =
𝑥 ∗ 𝑥 𝑛−1 , 𝑛 > 0 int main() {
int n,x;
printf("Introduceti valoarea pentru x (reala):");
scanf("%d", &x);
printf("Introduceti valoarea pentru n (>=0):");
scanf("%d", &n);
printf("x la puterea n este %lf\n", pow(x,n));
return 0;
}
Recursivitate
Funcția pwr face două calcule:
– un test (n == 0 condiția de oprire) dacă da, returnează 1
– altfel, se realizează o înmulțire; operandul drept necesită un nou apel, recursiv
Exemplu:
pwr(5, 3) apel↓↑125
5* pwr(5, 2) apel↓↑25
5* pwr(5, 1) apel↓↑5
5* pwr(5, 0) apel↓↑ 1
Recursivitate
În calculul recursiv al funcției putere:
◦ Fiecare apel face “în cascadă” un nou apel, până la cazul de bază
◦ Fiecare apel execută același cod, dar cu alte date (valori proprii pentru parametri)
◦ Ajunși la cazul de bază (condiția de oprire), toate apelurile începute sunt încă neterminate
(fiecare mai are de făcut înmulțirea cu rezultatul apelului efectuat)
◦ Revenirea se face în ordine inversă apelării (apelul cu exponent 0 revine primul, apoi cel cu
exponent 1, etc.)
Zona rezervată

Recursivitate Stiva

Cum se realizează transmiterea de parametri și revenirea din apeluri?


Să ne amintim, structura memoriei unui program! …
Să ne amintim: variabile globale (segmentul de date)/ variabile locale
(stivă)
Stiva:
◦ Este o zonă de memorie: Heap
◦ limitată!
◦ în care se salvează, următoarele informații pentru fiecare apel de
funcție:
◦ variabilele locale Segmentul de date
◦ argumentele funcției
◦ adresa de retur

Segmentul de cod
Spațiu pentru valoarea
Directia de creștere de retur
Variabile locale
Recursivitate Funcția apelantă
Parametru_n

Modul de funcționare al unei stive este LIFO (last Parametru_2
in first out). Parametru_1
Figura alăturată este o reprezentare a unei stive în Adresa de retur
momentul apelului unei funcții.
Pointer fereastra de
Întotdeauna trebuie avut grijă în lucrul cu funcţii memorie anterioara
recursive deoarece, la fiecare apel recursiv, Spațiu pentru valoarea
Funcția apelată de retur
contextul este salvat pe stivă pentru a putea fi
refăcut la revenirea din recursivitate. Variabile locale
În funcţie de numărul apelurilor recursive şi de
dimensiunea contextului (variabile, descriptori de
fişier, etc.) stiva se poate umple foarte rapid, Vârful stivei
generând o eroare de tip ”stack overflow”. Varful stivei
- -
Retur
Apel 5;3 5;3
Adresa corespunzatoare Adresa corespunzatoare
instructiunii de pe linia 6 instructiunii de pe linia 6

#include <stdio.h> - -
1
double pow(double x, int n) 2 5;2 5;2
{ 3 Adresa corespunzatoare Adresa corespunzatoare
if (n == 0) 4 instructiunii de pe linia 6 instructiunii de pe linia 6
return 1; 5
return x*pow(x, n - 1); 6 - -
} 7
5;1 5;1
Adresa corespunzatoare Adresa corespunzatoare
instructiunii de pe linia 6 instructiunii de pe linia 6
Exemplu apel pow(5,3)
- 1
5;0 5;0
Adresa corespunzatoare Adresa corespunzatoare
instructiunii de pe linia 6 instructiunii de pe linia 6
- -
Retur
Apel 5;3 5;3
Adresa corespunzatoare Adresa corespunzatoare
instructiunii de pe linia 6 instructiunii de pe linia 6

#include <stdio.h> - -
1
double pow(double x, int n) 2 5;2 5;2
{ 3 Adresa corespunzatoare Adresa corespunzatoare
if (n == 0) 4 instructiunii de pe linia 6 instructiunii de pe linia 6
return 1; 5
return x*pow(x, n - 1); 6 - 5
} 7
5;1 5;1
Adresa corespunzatoare Adresa corespunzatoare
instructiunii de pe linia 6 instructiunii de pe linia 6
Exemplu apel pow(5,3)
-
5;0
Adresa corespunzatoare
instructiunii de pe linia 6
- -
Retur
Apel 5;3 5;3
Adresa corespunzatoare Adresa corespunzatoare
instructiunii de pe linia 6 instructiunii de pe linia 6

#include <stdio.h> - 25
1
double pow(double x, int n) 2 5;2 5;2
{ 3 Adresa corespunzatoare Adresa corespunzatoare
if (n == 0) 4 instructiunii de pe linia 6 instructiunii de pe linia 6
return 1; 5
return x*pow(x, n - 1); 6 -
} 7
5;1
Adresa corespunzatoare
instructiunii de pe linia 6
Exemplu apel pow(5,3)
-
5;0
Adresa corespunzatoare
instructiunii de pe linia 6
- 125
Retur
Apel 5;3 5;3
Adresa corespunzatoare Adresa corespunzatoare
instructiunii de pe linia 6 instructiunii de pe linia 6

#include <stdio.h> -
1
double pow(double x, int n) 2 5;2
{ 3 Adresa corespunzatoare
if (n == 0) 4 instructiunii de pe linia 6
return 1; 5
return x*pow(x, n - 1); 6 -
} 7
5;1
Adresa corespunzatoare
instructiunii de pe linia 6
Exemplu apel pow(5,3)
-
5;0
Adresa corespunzatoare
instructiunii de pe linia 6
Recursivitate #include <stdio.h>
long factorial(int n)
{
Alt exemplu de funcție recursivă:
if (n == 0)
factorial: return 1; //conditia de oprire
else
N->N return(n * factorial(n - 1));
}
1, 𝑛 = 0
n!= int main() {
𝑛 ∗ 𝑛 − 1 !, 𝑛 > 0 int n;
printf("Introduceti valoarea pentru n (>=0):");
scanf("%d", &n);
printf("n! = %ld\n", factorial(n));
return 0;
}
Recursivitate #include <stdio.h>
long Fib(int n){
if (n == 0)
Alt exemplu de funcție recursivă:
return 0;
fibonacci: if (n == 1)
return 1;
N->N else
0, 𝑛 = 0 return Fib(n - 1) + Fib(n - 2);
}
Fib(n)= 1, 𝑛 = 1
int main() {
𝐹𝑖𝑏 𝑛 − 1 + 𝐹𝑖𝑏(𝑛 − 2) int n;
printf("Introduceti valoarea pentru n (>=0):");
scanf("%d", &n);
Atenție: În acest exemplu avem două printf("Fibonacci(n) = %ld\n", Fib(n));
cazuri de bază, deoarece formula de return 0;
recurență folosește doi termeni. }
#include <stdio.h>
void citeste_recursiv(int nr)
{
if (nr <= 0) /*se citesc
Recursivitate doar numere naturale*/
return;
Cazuri de folosire a recursivității: else
◦ Implementarea unor funcții/noțiuni recursive {
printf("\nIntroduceti un
◦ Divizarea unei probleme în subprobleme de numar stric pozitiv (>0) sau
același tip (tehnica divide et impera). 0 pentru a termina
◦ Afișarea/prelucrarea unor elemente în ordine programul:");
inversă scanf("%d", &nr);
citeste_recursiv(nr);
Exemplu de prelucrare a elementelor în ordine }
inversă. if (nr>0)
Pentru prelucrarea elementelor în ordine inversă ne printf("%d ", nr);
folosim de stiva sistem. }
int main() {
Pentru a implementa funcția în variantă iterativă int n=1;
trebuie să ne folosim de structuri suplimentare citeste_recursiv(n);
(stivă/tablou). return 0;
}
Recursivitate vs. iterație
De multe ori, soluția unei probleme poate fi elaborată mult mai ușor, mai clar și mai simplu de
verificat, printr-un algoritm recursiv.
Dar, cel mai mare dezavantaj al recursivității este folosirea stivei, care se poate umple foarte rapid.
=> Atenție: Numeroase platforme de calcul (îndeosebi cele încorporate) nu suportă recursivitatea.
Alternativă:
Transformarea recursivității în iterații.
Orice program recursiv poate fi transformat în unul iterativ!
Recursivitate vs. iterație
RECURSIVITATE ITERAȚIE

◦ fiecare apel creează noi copii de parametri ◦ control direct al iterațiilor


cu alte valori ◦ se modifică prin atribuire valorile
◦ se execută din nou aceeași funcție cu alte variabilelor
date ◦ se reia execuția acelorlași instrucțiuni cu
alte valori
Recursivitate vs. iterație
RECURSIVITATE ITERAȚIE

Avantaje Avantaje
◦ Ușor de implementat – nu este necesară o ◦ Consum redus de memorie – necesar în
structură de date (se folosește stiva cazul dispozitivelor cu memorie puțină
implicită)
◦ Ieșirea din ciclu se poate face facil, folosind
◦ Cod lizibil și scurt
instrucțiunea break
Dezavantaje
◦ Consum mare (chiar excesiv) de memorie Dezavantaje
◦ Ieșirea din ciclu poate fi dificilă, dacă nu ◦ Cod lung
sunt bune condițiile ◦ Poate necesita structuri de date auxiliare
◦ Optimizarea memoriei presupune, în
general, folosirea de variabile globale
#include <stdio.h>
double pow(double x, int n) { //recursiva
Recursivitate vs. if (n == 0)
return 1;
iterație }
return x*pow(x, n - 1);

double pow_iterativ(double x, int n){ //iterativa


Tehnici de transformare a long r = 1; //cazul de baza, valoare initiala
recursivității în iterație: while (n>0) //conditia de continuare
În cazul recursivității cu rezultat {
parțial: r = x*r; //acumulare
n = n-1;
◦ Valoarea inițială a rezultatului
rămâne aceeași }
return r;
◦ Testul de oprire al iterației poate fi
}
același cu cazul de bază al
recursivității int main() {
int n, x;
◦ Valoarea rezultatului final este
printf("Introduceti valoarea pentru x (reala):");
obținută tot prin acumulare
scanf("%d", &x);
printf("Introduceti valoarea pentru n (>=0):");
scanf("%d", &n);
printf("x la puterea n este %lf\n", pow_iterativ(x, n));
return 0;
}
#include <stdio.h>
long factorial(int n)
Recursivitate vs. {
if (n == 0)
iterație else
return 1; //conditia de oprire

return(n * factorial(n - 1));


Funcția factorial în variantă }
recursivă vs. varianta iterativă long factorial_i(int n) //iterativ
Implementare variantă iterativă cu {
ajutorul instrucțiunii ciclice for. long f = 1; //cazul de baza
int i;
for (i = 1; i <= n; i++)
f = f*i;
return f;
}
int main() {
int n;
printf("Introduceti valoarea pentru n (>=0):");
scanf("%d", &n);
printf("n! = %ld\n", factorial_i(n));
return 0;
}
Exerciții
E4_1: Realizați câte un proiect cu exemplele de cod din acest curs și urmăriți cu ajutorul
depanatorului de programe (debugger) execuția programelor linie cu linie, urmărind totodată și
conținutul stivei. Ce se întâmplă dacă în cazul funcțiilor recursive parametrul este eliminat și
înlocuit cu o variabilă globală (int n devine variabilă globală)?
E4_2: Implementați funcțiile recursive din acest curs (cu excepția funcției citește_recursiv) în
variantă iterativă.

Pentru mai multe informații despre subiectele abordate în acest curs accesați:
http://staff.cs.upt.ro/~marius/curs/pc/old/notes2.pdf
Bibliografie selectivă
◦ Kernighan, B. W., & Ritchie, D. ”The C programming language - Second
edition”, 1988 Prentice Hall Software Series
◦ Minea, M., Limbaje de programare, materiale de curs
http://staff.cs.upt.ro/~marius/curs/lp/index.html
◦ Holotescu, C., Limbaje de programare, materiale de curs
http://labs.cs.upt.ro/~oose/pmwiki.php/LP/Lectures
◦ Iorga,V., Programarea Calculatoarelor, materiale de curs
http://andrei.clubcisco.ro/cursuri/anul-1/semestrul-1/programarea-
calculatoarelor.html

S-ar putea să vă placă și