Subprogramele Predefinite (Funcții de Sistem) în C++
1. Obiectivele lecției:
- Să înțeleagă conceptul de subprograme predefinite în C++.
- Să învețe cum să utilizeze funcțiile de sistem pentru diverse operații.
- Să implementeze exemple practice cu funcții predefinite din biblioteci standard.
2. Ce sunt subprogramele predefinite?
- Definiție: Subprogramele predefinite, cunoscute și ca funcții de sistem, sunt funcții oferite de bibliotecile standard ale limbajului C++ pentru a realiza operații comune fără a fi nevoie să fie implementate de utilizator.
- Exemple de utilizare: Operații matematice, manipularea șirurilor, gestionarea fișierelor, manipularea timpului, și altele.
3. Categorii de funcții predefinite
1. Funcții matematice (<cmath> sau <math.h>)
Funcție | Descriere | Exemplu |
sqrt(x) | Rădăcina pătrată a lui x. | sqrt(16) -> 4.0 |
pow(x, y) | Ridică x la puterea y. | pow(2, 3) -> 8.0 |
abs(x) | Valoarea absolută a lui x. | abs(-5) -> 5 |
ceil(x) | Rotunjire în sus la cel mai apropiat întreg. | ceil(4.2) -> 5.0 |
floor(x) | Rotunjire în jos la cel mai apropiat întreg. | floor(4.8) -> 4.0 |
log(x) | Logaritm natural al lui x. | log(10) -> 2.3026 |
exp(x) | Ridică e la puterea x. | exp(1) -> 2.718 |
sin(x), cos(x), tan(x) | Funcții trigonometrice. | sin(3.14) -> ~0.0 |
Exemplu: Utilizarea funcțiilor matematice
#include <iostream>
#include <cmath>
using namespace std;
int main() {
double numar = 25.0;
cout << „Rădăcina pătrată: ” << sqrt(numar) << endl;
cout << „Puterea a doua: ” << pow(numar, 2) << endl;
cout << „Valoarea absolută: ” << abs(-numar) << endl;
return 0;
}
2. Funcții pentru șiruri de caractere (<cstring>)
Funcție | Descriere | Exemplu |
strlen(sir) | Returnează lungimea șirului. | strlen(„abc”) -> 3 |
strcmp(sir1, sir2) | Compară două șiruri lexicografic. | strcmp(„abc”, „abd”) -> -1 |
strcpy(dest, src) | Copiază șirul src în dest. | strcpy(dest, „hello”) |
strcat(dest, src) | Concatenează șirul src la sfârșitul lui dest. | strcat(dest, ” world”) |
strtok(sir, delim) | Împarte un șir în segmente. | strtok(„a,b,c”, „,”) -> „a” |
Exemplu: Funcții pentru șiruri
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char sir1[50] = „Salut”;
char sir2[] = ” lume!”;
strcat(sir1, sir2);
cout << „Concatenare: ” << sir1 << endl;
cout << „Lungime: ” << strlen(sir1) << endl;
return 0;
}
3. Funcții pentru manipularea timpului (<ctime>)
Funcție | Descriere | Exemplu |
time(NULL) | Returnează timpul curent în secunde de la epoca UNIX. | time(NULL) -> 1672444800 |
localtime(&time) | Conversie timp în structura tm (timp local). | localtime(&t) |
strftime(buffer, size, format, &time) | Formatare timp. | strftime(buf, 80, „%Y-%m-%d”, &t) |
clock() | Returnează timpul procesorului folosit de program. | clock() |
Exemplu: Afișarea timpului curent
#include <iostream>
#include <ctime>
using namespace std;
int main() {
time_t timpCurent = time(NULL);
tm* timpLocal = localtime(&timpCurent);
cout << „Ora curenta: ” << timpLocal->tm_hour << „:”
<< timpLocal->tm_min << „:” << timpLocal->tm_sec << endl;
return 0;
}
4. Funcții pentru generarea numerelor aleatoare (<cstdlib>)
Funcție | Descriere | Exemplu |
rand() | Generează un număr aleator între 0 și RAND_MAX. | rand() |
srand(seed) | Inițializează generatorul cu un număr specific (seed). | srand(time(NULL)) |
Exemplu: Generarea numerelor aleatoare
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
srand(time(NULL));
for (int i = 0; i < 5; i++) {
cout << „Numar aleator: ” << rand() % 100 << endl; // Număr între 0 și 99
}
return 0;
}
5. Activități practice pentru elevi
- Scrieți un program care calculează aria unui cerc folosind funcția pow și M_PI (constantă din <cmath>).
- Realizați un program care verifică dacă două șiruri sunt identice utilizând funcția strcmp.
- Implementați un program care generează 10 numere aleatoare între 1 și 50 și le afișează sortate.
6. Scheme logice
- Calcul matematic:
- Start -> Apel funcție matematică -> Returnează rezultat -> Stop.
- Manipularea șirurilor:
- Start -> Citire șiruri -> Apel funcție (strlen, strcat, etc.) -> Afișare rezultat -> Stop.
- Generare aleatorie:
- Start -> Inițializare generator -> Generare număr -> Afișare -> Stop.
7. Concluzie
- Funcțiile predefinite din C++ oferă soluții rapide și eficiente pentru probleme comune.
- Alegerea funcției corecte depinde de tipul de problemă: matematică, manipulare șiruri, timp sau generare aleatoare.
- Practica utilizării funcțiilor predefinite ajută la dezvoltarea rapidă a programelor.
Subprograme Definite de Utilizator în C++
1. Obiectivele lecției:
- Să înțeleagă conceptul de subprograme definite de utilizator în C++.
- Să învețe să declare, să definească și să utilizeze funcții create de utilizator.
- Să implementeze exemple practice care folosesc funcții pentru a rezolva probleme comune.
2. Ce sunt subprogramele definite de utilizator?
- Definiție: Subprogramele definite de utilizator sunt funcții create de programator pentru a rezolva probleme specifice. Acestea sunt module independente care pot fi apelate de mai multe ori în program, reducând redundanța și îmbunătățind lizibilitatea codului.
- Avantaje:
- Reutilizare de cod.
- Organizare mai bună a programului.
- Debugging mai simplu.
3. Structura unei funcții în C++
- Declarație (prototip):
tip_returnare nume_functie(parametrii_opționali);
- Definiție:
tip_returnare nume_functie(parametrii_opționali) {
// Bloc de instrucțiuni
return valoare; // opțional, dacă tipul de returnare este void
}
- Apelare:
nume_functie(argumente_opționale);
4. Categorii de funcții
- Funcții fără parametri și fără returnare:
- Utilizate pentru operații simple care nu necesită intrări sau ieșiri.
Exemplu:
#include <iostream>
using namespace std;
void salut() {
cout << „Salut, lume!” << endl;
}
int main() {
salut(); // Apel funcție
return 0;
}
- Funcții cu parametri, dar fără returnare:
- Utilizate pentru operații care necesită intrări, dar nu returnează valori.
Exemplu:
#include <iostream>
using namespace std;
void afiseazaSuma(int a, int b) {
cout << „Suma este: ” << a + b << endl;
}
int main() {
afiseazaSuma(3, 5);
return 0;
}
- Funcții fără parametri, dar cu returnare:
- Returnează o valoare calculată, fără a avea nevoie de intrări.
Exemplu:
#include <iostream>
using namespace std;
int daNumarAleator() {
return 42;
}
int main() {
cout << „Numărul generat: ” << daNumarAleator() << endl;
return 0;
}
- Funcții cu parametri și cu returnare:
- Cele mai versatile funcții, care primesc intrări și returnează o valoare.
Exemplu:
#include <iostream>
using namespace std;
int inmulteste(int a, int b) {
return a * b;
}
int main() {
int rezultat = inmulteste(4, 5);
cout << „Rezultatul este: ” << rezultat << endl;
return 0;
}
5. Transmiterea parametrilor
- Prin valoare:
- Parametrii sunt copiați, iar modificările nu afectează variabilele originale.
Exemplu:
void modifica(int x) {
x = 10;
}
- Prin referință:
- Se transmite adresa variabilei, iar modificările afectează variabila originală.
Exemplu:
void modifica(int& x) {
x = 10;
}
- Prin pointer:
- Similar cu referința, dar se folosește sintaxa pointerilor.
Exemplu:
void modifica(int* x) {
*x = 10;
}
6. Funcții recursive
- Definiție: O funcție care se apelează pe ea însăși pentru a rezolva o problemă divizată în subprobleme.
- Exemplu: Factorial
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
return n * factorial(n – 1);
}
int main() {
cout << „Factorial de 5: ” << factorial(5) << endl;
return 0;
}
7. Exemple practice
- Funcție pentru determinarea maximului dintre două numere:
int maxim(int a, int b) {
return (a > b) ? a : b;
}
- Funcție pentru verificarea unui număr prim:
bool estePrim(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
- Funcție pentru calculul sumei elementelor dintr-un vector:
int sumaVector(int vec[], int dim) {
int suma = 0;
for (int i = 0; i < dim; i++) {
suma += vec[i];
}
return suma;
}
8. Activități practice pentru elevi
- Scrieți o funcție care determină dacă un număr este par sau impar.
- Realizați o funcție care calculează suma cifrelor unui număr.
- Implementați o funcție care verifică dacă un șir este palindrom.
9. Scheme logice
- Funcție simplă:
- Start -> Parametru -> Instrucțiuni -> Returnare (opțional) -> Stop.
- Funcție recursivă:
- Start -> Verificare condiție de bază -> Apel recursiv -> Returnare rezultat -> Stop.
10. Concluzie
- Subprogramele definite de utilizator sunt esențiale pentru a structura și organiza programele complexe.
- Alegerea parametrilor și a tipului de returnare depinde de cerințele problemei.
- Practica și utilizarea subprogramelor ajută la dezvoltarea unor soluții modulare și eficiente.
Variabile Locale și Globale. Domeniu de Vizibilitate în C++
1. Obiectivele lecției:
- Să înțeleagă diferența dintre variabilele locale și globale.
- Să înțeleagă conceptul de domeniu de vizibilitate.
- Să implementeze exemple practice pentru utilizarea corectă a variabilelor locale și globale.
2. Variabile locale
- Definiție:
- Variabilele locale sunt declarate în interiorul unui bloc de cod, cum ar fi o funcție sau un bloc delimitat de {}.
- Sunt accesibile doar în interiorul blocului în care au fost declarate.
- Caracteristici:
- Sunt create atunci când blocul de cod începe execuția și sunt distruse când blocul se termină.
- Au o vizibilitate limitată la blocul în care sunt definite.
- Exemplu:
#include <iostream>
using namespace std;
void afiseaza() {
int x = 10; // Variabilă locală
cout << „Valoarea lui x: ” << x << endl;
}
int main() {
afiseaza();
// cout << x; // Eroare: x nu este vizibil în main
return 0;
}
3. Variabile globale
- Definiție:
- Variabilele globale sunt declarate în afara oricărei funcții și sunt accesibile în orice parte a programului, după punctul lor de declarație.
- Caracteristici:
- Sunt create odată cu începutul programului și distruse odată cu terminarea programului.
- Pot fi accesate și modificate de oriunde, ceea ce poate duce la conflicte dacă nu sunt utilizate cu atenție.
- Exemplu:
#include <iostream>
using namespace std;
int x = 20; // Variabilă globală
void afiseaza() {
cout << „Valoarea lui x în afiseaza(): ” << x << endl;
}
int main() {
cout << „Valoarea lui x în main(): ” << x << endl;
afiseaza();
return 0;
}
4. Domeniul de vizibilitate (Scope)
- Definiție:
- Domeniul de vizibilitate reprezintă partea programului în care o variabilă poate fi accesată.
- Tipuri de domeniu de vizibilitate:
- Bloc local: Vizibilitate limitată la blocul în care variabila este declarată.
- Bloc global: Vizibilitate pe tot parcursul programului, după declarație.
- Blocuri imbricate: Variabilele declarate în blocuri interioare ascund variabilele cu același nume din blocurile exterioare.
5. Conflicte între variabile locale și globale
- Utilizarea numelor identice:
- O variabilă locală cu același nume ca o variabilă globală va ascunde variabila globală în interiorul blocului său.
- Exemplu:
#include <iostream>
using namespace std;
int x = 50; // Variabilă globală
void afiseaza() {
int x = 10; // Variabilă locală
cout << „Variabilă locală x: ” << x << endl;
cout << „Variabilă globală x: ” << ::x << endl; // Accesare variabilă globală
}
int main() {
afiseaza();
cout << „Variabilă globală x în main(): ” << x << endl;
return 0;
}
6. Avantaje și dezavantaje
- Variabile locale:
- Avantaje:
- Evită conflictele dintre funcții.
- Utilizează memoria doar pe durata blocului în care sunt folosite.
- Dezavantaje:
- Nu sunt accesibile din alte funcții sau părți ale programului.
- Avantaje:
- Variabile globale:
- Avantaje:
- Accesibile din orice funcție, facilitând partajarea datelor.
- Dezavantaje:
- Pot duce la conflicte dacă sunt folosite neglijent.
- Pot face debugging-ul mai dificil.
- Avantaje:
7. Exemple practice
- Exemplu: Utilizarea variabilelor globale pentru configurări comune
#include <iostream>
using namespace std;
const int MAX_VAL = 100; // Variabilă globală constantă
void verifica(int val) {
if (val > MAX_VAL) {
cout << „Valoarea depășește limita maximă!” << endl;
} else {
cout << „Valoarea este validă.” << endl;
}
}
int main() {
int valoare = 120;
verifica(valoare);
return 0;
}
- Exemplu: Preferința pentru variabile locale
#include <iostream>
using namespace std;
int suma(int a, int b) {
int rezultat = a + b; // Variabilă locală
return rezultat;
}
int main() {
cout << „Suma: ” << suma(5, 7) << endl;
return 0;
}
8. Activități practice pentru elevi
- Scrieți un program care utilizează o variabilă globală pentru a număra câte funcții au fost apelate.
- Realizați un program care demonstrează cum o variabilă locală ascunde o variabilă globală cu același nume.
- Implementați un program care folosește variabile locale pentru a calcula produsul elementelor unui vector.
9. Scheme logice
- Accesarea variabilelor:
- Start -> Declarare variabilă globală -> Declarare variabilă locală -> Verificare accesibilitate -> Stop.
- Conflicte între variabile:
- Start -> Declarare variabile cu același nume (global/local) -> Utilizare variabilă locală -> Accesare explicită a variabilei globale -> Stop.
10. Concluzie
- Variabilele locale sunt utilizate pentru scopuri specifice, limitând vizibilitatea acestora la un bloc.
- Variabilele globale sunt utile pentru date care trebuie partajate între mai multe funcții, dar pot genera conflicte dacă nu sunt gestionate cu atenție.
- Alegerea tipului de variabilă depinde de cerințele programului, iar o bună practică este utilizarea minimă a variabilelor globale.
Mecanismul de Transmitere a Datelor prin Parametrii în C++
1. Obiectivele lecției:
- Să înțeleagă conceptul de parametri formali și actuali.
- Să exploreze metodele de transmitere a datelor: prin valoare, prin referință și prin adresă.
- Să implementeze exemple practice pentru fiecare tip de apel.
2. Parametrii formali și parametrii actuali
- Parametrii formali:
- Variabilele declarate în lista de parametri a funcției.
- Reprezintă datele de intrare așteptate de funcție.
- Există doar pe durata execuției funcției.
Exemplu:
void afiseazaSuma(int a, int b) { // a și b sunt parametri formali
cout << „Suma este: ” << a + b << endl;
}
- Parametrii actuali:
- Valorile sau variabilele transmise funcției în momentul apelului.
- Se asociază cu parametrii formali.
Exemplu:
int main() {
afiseazaSuma(3, 5); // 3 și 5 sunt parametri actuali
return 0;
}
3. Metode de transmitere a parametrilor
1. Apel prin valoare
- Definiție:
- Parametrul formal primește o copie a valorii parametrului actual.
- Modificările asupra parametrului formal nu afectează parametrul actual.
- Exemplu:
void modificaValoare(int x) {
x = 10; // Parametrul actual nu este afectat
}
int main() {
int numar = 5;
modificaValoare(numar);
cout << „Valoarea lui numar: ” << numar << endl; // Afișează 5
return 0;
}
- Avantaje:
- Simplu de utilizat.
- Parametrul actual rămâne neschimbat.
- Dezavantaje:
- Ineficient pentru tipuri mari de date (copierea consumă timp și memorie).
2. Apel prin referință
- Definiție:
- Parametrul formal devine un alias pentru parametrul actual.
- Modificările asupra parametrului formal afectează direct parametrul actual.
- Exemplu:
void modificaReferinta(int& x) {
x = 10; // Parametrul actual este modificat
}
int main() {
int numar = 5;
modificaReferinta(numar);
cout << „Valoarea lui numar: ” << numar << endl; // Afișează 10
return 0;
}
- Avantaje:
- Eficient (nu se face copiere).
- Permite modificarea valorii originale.
- Dezavantaje:
- Poate duce la erori dacă modificările sunt nedorite.
3. Apel prin adresă
- Definiție:
- Parametrul formal primește adresa parametrului actual.
- Accesul și modificarea se fac prin dereferențiere.
- Exemplu:
void modificaAdresa(int* x) {
*x = 10; // Parametrul actual este modificat
}
int main() {
int numar = 5;
modificaAdresa(&numar); // Transmite adresa lui numar
cout << „Valoarea lui numar: ” << numar << endl; // Afișează 10
return 0;
}
- Avantaje:
- Eficient și flexibil (permite modificări și acces la memoria parametrului actual).
- Dezavantaje:
- Sintaxa mai complicată.
- Necesită atenție pentru a evita erorile legate de pointeri.
4. Compararea metodelor de transmitere
Caracteristică | Apel prin valoare | Apel prin referință | Apel prin adresă |
Modificarea parametrului actual | Nu | Da | Da |
Eficiență pentru tipuri mari | Scăzută | Ridicată | Ridicată |
Simplitate | Simplă | Moderată | Mai complicată |
5. Exemple practice
Exemplu 1: Schimbarea valorilor a două variabile
- Prin referință:
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
swap(x, y);
cout << „x: ” << x << „, y: ” << y << endl; // x: 10, y: 5
return 0;
}
- Prin adresă:
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
swap(&x, &y);
cout << „x: ” << x << „, y: ” << y << endl; // x: 10, y: 5
return 0;
}
Exemplu 2: Calcularea sumei elementelor unui vector
- Prin referință:
int suma(const int& a, const int& b) {
return a + b;
}
int main() {
int x = 5, y = 10;
cout << „Suma: ” << suma(x, y) << endl; // Suma: 15
return 0;
}
6. Activități practice pentru elevi
- Scrieți un program care folosește apelul prin referință pentru a tripla valoarea unei variabile.
- Implementați o funcție care folosește apelul prin adresă pentru a calcula produsul a două numere.
- Realizați un program care calculează suma elementelor unui vector, folosind apelul prin valoare și prin referință.
7. Scheme logice
- Apel prin valoare:
- Start -> Transmite copie -> Modifică copia -> Nu afectează originalul -> Stop.
- Apel prin referință:
- Start -> Transmite referință -> Modifică variabila originală -> Stop.
- Apel prin adresă:
- Start -> Transmite adresa -> Dereferențiere și modificare -> Stop.
8. Concluzie
- Alegerea metodei de transmitere a parametrilor depinde de cerințele programului:
- Prin valoare: Pentru date care nu trebuie modificate și sunt mici.
- Prin referință: Pentru date mari care necesită modificări.
- Prin adresă: Pentru cazuri complexe care implică acces la locația memoriei.
- Practica este esențială pentru a înțelege și aplica corect fiecare metodă.
Clasificarea Funcțiilor Definite de Utilizator: Funcții Procedurale și Funcții Operand
1. Obiectivele lecției:
- Să înțeleagă diferența dintre funcțiile procedurale și funcțiile operand.
- Să identifice utilizarea fiecărui tip de funcție.
- Să implementeze exemple practice pentru a evidenția diferențele.
2. Ce sunt funcțiile definite de utilizator?
- Funcțiile definite de utilizator sunt module create pentru a rezolva probleme specifice. Acestea pot fi clasificate în funcții procedurale și funcții operand pe baza scopului lor și a modului în care sunt utilizate.
3. Clasificarea funcțiilor
1. Funcții Procedurale
- Definiție:
- Sunt funcții care realizează o serie de operații, fără a returna o valoare semnificativă.
- Rezultatul lor este de obicei afișat sau utilizat pentru efecte secundare (ex.: modificarea unei structuri de date, afișarea unui mesaj).
- Caracteristici:
- Tipul de returnare este de obicei void.
- Utilizate pentru sarcini care nu necesită o valoare de ieșire.
- Exemple de utilizare:
- Afișarea unui mesaj.
- Modificarea unui vector.
- Scrierea în fișiere.
- Exemplu:
#include <iostream>
using namespace std;
void afiseazaMesaj() {
cout << „Aceasta este o funcție procedurală.” << endl;
}
int main() {
afiseazaMesaj();
return 0;
}
2. Funcții Operand
- Definiție:
- Sunt funcții care efectuează o operație și returnează un rezultat.
- Rezultatul este utilizat direct în program.
- Caracteristici:
- Tipul de returnare este diferit de void (ex.: int, double, string).
- Pot fi utilizate în expresii sau pentru atribuire.
- Exemple de utilizare:
- Calculul unei sume.
- Determinarea unui număr maxim.
- Returnarea unei valori transformate.
- Exemplu:
#include <iostream>
using namespace std;
int suma(int a, int b) {
return a + b;
}
int main() {
int rezultat = suma(3, 5);
cout << „Suma este: ” << rezultat << endl;
return 0;
}
4. Diferențe între funcții procedurale și funcții operand
Caracteristică | Funcții Procedurale | Funcții Operand |
Tipul de returnare | void | Tipuri precum int, double |
Scop | Realizează operații sau efecte | Calculează și returnează valori |
Exemple | Afișare, modificări de date | Calcul, comparații, transformări |
Utilizare în expresii | Nu | Da |
5. Exemple practice
Exemplu 1: Modificarea unui vector (procedurală)
#include <iostream>
using namespace std;
void cresteElemente(int vec[], int dim) {
for (int i = 0; i < dim; i++) {
vec[i] += 1;
}
}
int main() {
int vector[5] = {1, 2, 3, 4, 5};
cresteElemente(vector, 5);
for (int i = 0; i < 5; i++) {
cout << vector[i] << ” „;
}
return 0;
}
Exemplu 2: Calcularea mediei unui vector (operand)
#include <iostream>
using namespace std;
double calculeazaMedia(int vec[], int dim) {
int suma = 0;
for (int i = 0; i < dim; i++) {
suma += vec[i];
}
return static_cast<double>(suma) / dim;
}
int main() {
int vector[5] = {1, 2, 3, 4, 5};
double media = calculeazaMedia(vector, 5);
cout << „Media este: ” << media << endl;
return 0;
}
6. Combinarea funcțiilor procedurale și operand
- Exemplu complet:
- Procedural: Afișarea unui rezultat.
- Operand: Calculul unui rezultat.
#include <iostream>
using namespace std;
double calculeazaMedia(int vec[], int dim) {
int suma = 0;
for (int i = 0; i < dim; i++) {
suma += vec[i];
}
return static_cast<double>(suma) / dim;
}
void afiseazaMedia(double media) {
cout << „Media calculată este: ” << media << endl;
}
int main() {
int vector[5] = {1, 2, 3, 4, 5};
double media = calculeazaMedia(vector, 5);
afiseazaMedia(media);
return 0;
}
7. Activități practice pentru elevi
- Scrieți o funcție procedurală care afișează toate elementele pare dintr-un vector.
- Implementați o funcție operand care determină produsul elementelor unui vector.
- Realizați un program care folosește o funcție operand pentru a calcula factorialul unui număr și o funcție procedurală pentru afișare.
8. Scheme logice
- Funcție procedurală:
- Start -> Realizează operația -> Fără returnare -> Stop.
- Funcție operand:
- Start -> Efectuează calculul -> Returnează rezultatul -> Stop.
9. Concluzie
- Funcțiile procedurale sunt utile pentru realizarea operațiilor fără a returna un rezultat semnificativ.
- Funcțiile operand sunt esențiale pentru calcularea și returnarea rezultatelor în expresii.
- O bună organizare a funcțiilor ajută la crearea de programe clare, eficiente și ușor de întreținut.
Subprograme Recursive – Funcții Direct Recursive în C++
1. Obiectivele lecției:
- Să înțeleagă conceptul de funcții recursive.
- Să înțeleagă ce sunt funcțiile direct recursive.
- Să implementeze exemple practice care folosesc recursivitatea directă pentru a rezolva probleme.
2. Ce este recursivitatea?
- Definiție:
- Recursivitatea este o tehnică de programare în care o funcție se apelează pe ea însăși pentru a rezolva o problemă prin divizarea acesteia în subprobleme mai simple.
- Condiții necesare pentru o funcție recursivă:
- Cazul de bază: O condiție care oprește recursivitatea.
- Cazul recursiv: Funcția se apelează pe ea însăși cu o versiune mai simplă a problemei.
3. Ce sunt funcțiile direct recursive?
- Definiție:
- O funcție este direct recursivă dacă se apelează pe ea însăși în interiorul definiției sale.
- Exemplu simplu:
void functieRecursiva() {
cout << „Aceasta este o functie recursiva directa.” << endl;
functieRecursiva(); // Apel direct recursiv
}
- Utilizare:
- Calcularea factorialului.
- Seria Fibonacci.
- Parcurgerea structurilor de date, cum ar fi arborii.
4. Avantajele și dezavantajele recursivității
Avantaje | Dezavantaje |
Simplifică soluția pentru probleme complexe. | Poate duce la depășirea stivei de apeluri (stack overflow). |
Cod mai concis și mai lizibil pentru probleme recursive. | Consumă mai multă memorie din cauza apelurilor recursive. |
Ușor de aplicat pentru probleme de tip divide-and-conquer. | Performanță mai slabă decât soluțiile iterative în anumite cazuri. |
5. Exemple practice
Exemplu 1: Calcularea Factorialului
- Definiție matematică:
n!=n⋅(n−1)!n! = n \cdot (n – 1)!n!=n⋅(n−1)!, cu 0!=10! = 10!=1. - Implementare:
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1; // Cazul de bază
}
return n * factorial(n – 1); // Apel recursiv
}
int main() {
int numar = 5;
cout << „Factorial de ” << numar << ” este: ” << factorial(numar) << endl;
return 0;
}
Exemplu 2: Seria Fibonacci
- Definiție matematică:
F(n)=F(n−1)+F(n−2)F(n) = F(n – 1) + F(n – 2)F(n)=F(n−1)+F(n−2), cu F(0)=0F(0) = 0F(0)=0 și F(1)=1F(1) = 1F(1)=1. - Implementare:
#include <iostream>
using namespace std;
int fibonacci(int n) {
if (n == 0) {
return 0; // Cazul de bază
}
if (n == 1) {
return 1; // Cazul de bază
}
return fibonacci(n – 1) + fibonacci(n – 2); // Apel recursiv
}
int main() {
int numar = 6;
cout << „Al ” << numar << „-lea număr din seria Fibonacci este: ” << fibonacci(numar) << endl;
return 0;
}
Exemplu 3: Suma elementelor unui vector
- Problema: Calculați suma elementelor unui vector folosind recursivitatea.
- Implementare:
#include <iostream>
using namespace std;
int sumaVector(int vec[], int dim) {
if (dim == 0) {
return 0; // Cazul de bază
}
return vec[dim – 1] + sumaVector(vec, dim – 1); // Apel recursiv
}
int main() {
int vector[] = {1, 2, 3, 4, 5};
int dim = 5;
cout << „Suma elementelor este: ” << sumaVector(vector, dim) << endl;
return 0;
}
Exemplu 4: Parcurgerea unui arbore binar (preordine)
- Problema: Parcurgeți un arbore binar folosind recursivitatea.
- Implementare:
#include <iostream>
using namespace std;
struct Nod {
int valoare;
Nod* stanga;
Nod* dreapta;
};
void parcurgePreordine(Nod* radacina) {
if (radacina == NULL) {
return; // Cazul de bază
}
cout << radacina->valoare << ” „;
parcurgePreordine(radacina->stanga); // Apel recursiv
parcurgePreordine(radacina->dreapta); // Apel recursiv
}
int main() {
Nod* radacina = new Nod{1, new Nod{2, NULL, NULL}, new Nod{3, NULL, NULL}};
cout << „Parcurgerea preordine: „;
parcurgePreordine(radacina);
return 0;
}
6. Probleme tipice rezolvate cu recursivitate
- Calcularea puterii unui număr: xn=x⋅xn−1x^n = x \cdot x^{n-1}xn=x⋅xn−1.
- Determinarea cifrei maxime dintr-un număr.
- Inversarea unui șir de caractere.
7. Activități practice pentru elevi
- Scrieți o funcție recursivă care calculează suma cifrelor unui număr.
- Implementați o funcție recursivă care verifică dacă un șir este palindrom.
- Scrieți o funcție recursivă care calculează valoarea maximă dintr-un vector.
8. Scheme logice
- Funcție recursivă:
- Start -> Verificare caz de bază -> Apel funcție pe subproblemă -> Combinare rezultat -> Stop.
- Funcție direct recursivă pentru factorial:
- Start -> Dacă n==0n == 0n==0, returnează 1 -> Apel recursiv n∗factorial(n−1)n * factorial(n – 1)n∗factorial(n−1) -> Stop.
9. Concluzie
- Funcțiile directe recursive sunt o soluție elegantă pentru probleme de tip divide-and-conquer.
- Este important să definim corect cazul de bază pentru a evita buclele infinite.
- Practica ajută la înțelegerea recursivității și la utilizarea acesteia în mod eficient.
Subprograme Recursive – Funcții Indirect Recursive în C++
1. Obiectivele lecției:
- Să înțeleagă conceptul de funcții indirect recursive.
- Să învețe diferențele dintre recursivitatea directă și cea indirectă.
- Să implementeze exemple practice de funcții indirect recursive.
2. Ce este recursivitatea indirectă?
- Definiție:
- Recursivitatea indirectă apare atunci când o funcție A apelează o funcție B, iar funcția B apelează din nou funcția A. Astfel, funcțiile implicate se apelează reciproc pentru a rezolva problema.
- Structura generală:
void A() {
// Operații
B(); // Funcția A apelează funcția B
}
void B() {
// Operații
A(); // Funcția B apelează funcția A
}
- Cerințe pentru recursivitatea indirectă:
- Definirea unui caz de bază pentru a opri apelurile recursive.
- Asigurarea unei secvențe finite de apeluri între funcții.
3. Diferența dintre recursivitatea directă și indirectă
Caracteristică | Recursivitate directă | Recursivitate indirectă |
Definiție | O funcție se apelează pe ea însăși. | Două sau mai multe funcții se apelează reciproc. |
Exemple comune | Factorial, Fibonacci | Funcții care validează reguli complexe. |
Complexitate | Mai simplă de implementat și înțeles. | Necesită planificare mai atentă. |
4. Exemple practice
Exemplu 1: Determinarea parității unui număr
- Problema: Determinați dacă un număr este par sau impar folosind recursivitate indirectă.
- Implementare:
#include <iostream>
using namespace std;
void esteImpar(int n);
void estePar(int n) {
if (n == 0) {
cout << „Numărul este par.” << endl;
return; // Caz de bază
}
esteImpar(n – 1); // Apel recursiv indirect
}
void esteImpar(int n) {
if (n == 0) {
cout << „Numărul este impar.” << endl;
return; // Caz de bază
}
estePar(n – 1); // Apel recursiv indirect
}
int main() {
int numar = 5;
estePar(numar); // Pornește determinarea
return 0;
}
Exemplu 2: Validarea unei secvențe de reguli
- Problema: Validarea unui șir care trebuie să alterneze între vocale și consoane.
- Implementare:
#include <iostream>
#include <cstring>
using namespace std;
void verificaConsoana(const char* sir, int index);
void verificaVocala(const char* sir, int index) {
if (sir[index] == ‘\0’) {
cout << „Șirul este valid.” << endl;
return; // Caz de bază
}
if (strchr(„aeiouAEIOU”, sir[index])) {
verificaConsoana(sir, index + 1); // Apel recursiv indirect
} else {
cout << „Șir invalid: așteptam o vocală la poziția ” << index << endl;
}
}
void verificaConsoana(const char* sir, int index) {
if (sir[index] == ‘\0’) {
cout << „Șirul este valid.” << endl;
return; // Caz de bază
}
if (!strchr(„aeiouAEIOU”, sir[index])) {
verificaVocala(sir, index + 1); // Apel recursiv indirect
} else {
cout << „Șir invalid: așteptam o consoană la poziția ” << index << endl;
}
}
int main() {
const char* sir = „aBcDeF”;
verificaVocala(sir, 0); // Pornește validarea
return 0;
}
Exemplu 3: Calculul sumelor alternative
- Problema: Calculați suma numerelor pozitive și negative dintr-un vector, alternând între apeluri.
- Implementare:
#include <iostream>
using namespace std;
int sumaNegative(const int vec[], int dim, int index);
int sumaPozitive(const int vec[], int dim, int index) {
if (index == dim) {
return 0; // Caz de bază
}
if (vec[index] > 0) {
return vec[index] + sumaNegative(vec, dim, index + 1); // Apel recursiv indirect
}
return sumaNegative(vec, dim, index + 1);
}
int sumaNegative(const int vec[], int dim, int index) {
if (index == dim) {
return 0; // Caz de bază
}
if (vec[index] < 0) {
return vec[index] + sumaPozitive(vec, dim, index + 1); // Apel recursiv indirect
}
return sumaPozitive(vec, dim, index + 1);
}
int main() {
int vector[] = {1, -2, 3, -4, 5};
int dim = 5;
int suma = sumaPozitive(vector, dim, 0);
cout << „Suma totală este: ” << suma << endl;
return 0;
}
5. Avantaje și dezavantaje ale recursivității indirecte
Avantaje | Dezavantaje |
Permite modelarea unor probleme complexe în mod elegant. | Poate fi dificil de urmărit fluxul de apeluri. |
Poate simplifica implementarea unor reguli interdependente. | Performanță mai scăzută decât soluțiile iterative. |
Ajută la separarea logicii în funcții mai mici și clare. | Crește riscul de depășire a stivei de apeluri. |
6. Activități practice pentru elevi
- Implementați două funcții recursive indirecte care determină dacă toate cifrele unui număr sunt impare sau pare.
- Scrieți un program care folosește recursivitatea indirectă pentru a calcula suma și produsul elementelor unui vector.
- Realizați două funcții care validează dacă un șir începe și se termină cu același caracter, alternând apelurile.
7. Scheme logice
- Fluxul recursivității indirecte:
- Start -> Funcția A -> Apelă funcția B -> Apelă funcția A -> Condiție de oprire -> Stop.
- Diagrama de apeluri:
A -> B -> A -> … -> Stop
8. Concluzie
- Recursivitatea indirectă este o tehnică utilă pentru probleme complexe care implică relații între mai multe funcții.
- Este important să definim clar cazul de bază pentru a preveni buclele infinite.
- Separarea logicii între funcții ajută la claritatea și modularitatea codului.