Subprograme (funcții)
Ideile de bază pe scurt și, unde există, exercițiile din subiectele de BAC care le verifică. Lecția completă, animată, cu grile și exerciții corectate, e în aplicația Tomomi.
Pe scurt
Sintaxă: antetul și apelul funcției
Tip returnat + nume + parametri formali, corp cu return, apel cu argumente în ordine:
int maxim(int a, int b) {
if (a > b) {
return a;
}
return b;
}
// apel: maxim(7, 3) -> 7Idee: return dă valoarea înapoi
return e oprește funcția și trimite e în expresia apelantului. Fără return, o funcție int nu produce nimic utilizabil - apelul ei într-o expresie este o greșeală. void este pentru acțiuni (afișări, modificări prin referință), nu pentru calcule.
Idee: locala moare la return
O variabilă locală există doar în timpul apelului: se creează la intrare și dispare la return. Același nume în main și în funcție înseamnă două variabile DIFERITE - locala o acoperă pe cea globală doar în interiorul funcției.
Capcana: funcția void nu intră în expresii
cout << f(3) cu f declarat void este o eroare: void nu produce nicio valoare. Folosește void doar pentru acțiuni (afișări, modificări prin referință); dacă ai nevoie de un rezultat, declară tipul returnat și pune return.
Șablon: interschimb prin referință
Aceeași interschimbare cu temporară, funcțională de data aceasta - mulțumită lui &:
void interschimb(int &a, int &b) {
int t = a;
a = b;
b = t;
}Capcana: fără & schimbi doar copia
void interschimb(int a, int b) pare identic, dar lucrează pe copii: în apelant, a și b rămân neschimbate. Semnul & face parametrul un alias al originalului - singura cale prin care o funcție poate modifica argumentul.
Fișă de sintaxă: subprogram în pseudocod
Definiția, apelul și corespondentul din C++:
subprogram nume(p1, p2)
...
întoarce e
■subprogram nume(...)- antetul. Nu se scriu tipuri, nici pentru parametri, nici pentru rezultat.întoarce e- trimite valoarea înapoi și încheie imediat subprogramul (echivalentul luireturn e;).■- închide definiția, la fel ca la orice bloc.- Apelul
nume(a, b)este o valoare: poate sta în orice expresie, de exemplus ← s + nume(a, b).
Capcana: parametrul este local
Un parametru cu același nume ca o variabilă din algoritmul principal NU este aceeași variabilă. Subprogramul primește o copie, deci a ← a * a în interior nu schimbă nimic afară - subiectele de examen mizează exact pe această confuzie.
A doua capcană: întoarce oprește subprogramul pe loc. Pus din greșeală în corpul unei bucle, algoritmul se întoarce după prima iterație, iar rezultatul pare „aproape corect” - de obicei valoarea de la primul pas.
Șablon: main organizează, subprogramele calculează
Tiparul de Subiectul III: un subprogram răspunde despre O valoare, iar main citește, apelează în buclă și afișează.
int test(int x) { // răspunde DESPRE o valoare
...
return rezultat;
}
int main() { // organizează
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> x;
if (test(x)) c = c + 1;
}
cout << c;
}Un subprogram poate apela alt subprogram deja definit mai sus: prim(x) se sprijină pe nrDiv(x) în loc să renumere divizorii.
Idee: un subprogram, o singură întrebare
Testul unei tăieturi bune: subprogramul trebuie să aibă sens și decupat din program. Primește tot ce îi trebuie prin parametri și întoarce un singur rezultat - nu citește singur de la tastatură și nu afișează nimic.
De aceea cin >> x; și cout << c; rămân în main, iar bucla care calculează coboară în subprogram. Când cerința se schimbă, modifici un singur apel, nu tot programul.
Din subiectele de BAC
Exerciții reale din subiectele anilor trecuți. Încearcă-le singur înainte să deschizi rezolvarea.
Numărul natural an este ascendent al numărului natural n dacă oricare dintre cifrele lui an este mai mare sau egală cu cifra unităților lui n.
Exemplu: oricare dintre numerele 7, 9, 98 sau 7998 este ascendent al lui 827, dar numărul 857 nu este ascendent al lui 827.
Subprogramul ascendent are trei parametri:
n, prin care primește un număr natural ();xșiy, prin care primește câte un număr natural din intervalul ().
Subprogramul returnează suma ascendenților lui n din intervalul , sau valoarea 0, dacă nu există niciun astfel de ascendent. Scrieți definiția completă a subprogramului C/C++.
Exemplu: dacă n=827, x=9, y=800, subprogramul returnează 7893 ().
Vezi rezolvarea
Cifra de comparație este cifra unităților lui n, adică n%10; ea se calculează o singură dată. Apoi parcurgem intervalul și, pentru fiecare număr, îi verificăm toate cifrele cu ciclul standard c%10 / c/10.
int ascendent(int n, int x, int y)
{
int u = n%10, s = 0, i, c, ok;
for (i = x; i <= y; i++)
{
c = i;
ok = 1;
if (c == 0)
ok = (u == 0);
while (c > 0)
{
if (c%10 < u)
ok = 0;
c = c/10;
}
if (ok)
s = s+i;
}
return s;
}Două detalii care se pierd ușor:
1. numărul 0 nu intră în bucla while (c > 0), deci ar fi declarat ascendent din oficiu; el are o singură cifră, 0, deci este ascendent numai dacă cifra unităților lui n este tot 0;
2. dacă nu există niciun ascendent, suma rămâne 0, deci chiar valoarea cerută de enunț - nu este nevoie de un caz separat.
Verificare pe exemplu: pentru n=827 cifra unităților este 7, iar în numerele cu toate cifrele sunt cele din enunț, cu suma 7893.
Un număr natural se numește major impar dacă suma divizorilor săi proprii impari este strict mai mare decât suma divizorilor săi proprii pari. Divizorii proprii ai unui număr sunt divizorii săi naturali diferiți de 1 și de el însuși. Exemplu: 18 este număr major impar (divizorii săi proprii pari sunt 2, 6, cei impari 3, 9, iar 3+9>2+6).
Subprogramul majImp are doi parametri, a și b, prin care primește câte un număr natural (). Subprogramul returnează cel mai mic număr major impar din intervalul [a,b], sau valoarea 0, dacă în interval nu există un astfel de număr. Scrieți în C/C++ definiția completă a subprogramului.
Exemplu: dacă a=16, b=30, atunci subprogramul returnează 18.
Vezi rezolvarea
Pentru fiecare număr n din interval calculăm suma divizorilor proprii impari și suma celor pari; divizorii proprii sunt cei din intervalul [2, n/2] care divid n (excludem 1 și n).
int majImp(int a, int b)
{
int n, d, sImpar, sPar;
for (n = a; n <= b; n++)
{
sImpar = 0;
sPar = 0;
for (d = 2; d <= n / 2; d++)
if (n % d == 0)
{
if (d % 2 == 0)
sPar = sPar + d;
else
sImpar = sImpar + d;
}
if (sImpar > sPar)
return n;
}
return 0;
}Parcurgem intervalul crescător, deci primul număr găsit este cel mai mic; dacă bucla se încheie fără să găsim un număr major impar, returnăm 0. Verificare pe exemplu: pentru n=18, divizorii proprii sunt 2, 3, 6, 9, cu 3+9=12 > 2+6=8, deci se returnează 18.
Un număr natural nenul, n, se numește număr abundent dacă , pentru orice număr natural nenul k (), unde s-a notat cu suma divizorilor pozitivi ai numărului natural nenul .
Subprogramul abundent are un singur parametru, n, prin care primește un număr natural (). Subprogramul returnează valoarea 1, dacă n este un număr abundent, sau valoarea 0, în caz contrar. Scrieți definiția completă a subprogramului.
Exemplu: pentru n=6, subprogramul returnează valoarea 1 (, iar cel mai mare raport obținut pentru valori strict mai mici decât 6 este ), iar pentru n=7 sau n=8, subprogramul returnează valoarea 0 (, ).
Vezi rezolvarea
Calculăm suma divizorilor cu un subprogram auxiliar, apoi comparăm raportul lui n cu maximul rapoartelor pentru , folosind împărțire reală:
int sumaDiv(int x) {
int s = 0;
for (int d = 1; d <= x; d++)
if (x % d == 0)
s = s + d;
return s;
}
int abundent(int n) {
double rmax = 0, r;
for (int k = 1; k < n; k++) {
r = 1.0 * sumaDiv(k) / k;
if (r > rmax)
rmax = r;
}
if (1.0 * sumaDiv(n) / n > rmax)
return 1;
return 0;
}Înmulțirea cu 1.0 forțează împărțirea reală (altfel raportul s-ar trunchia la întreg). Pentru : , iar maximul pentru este , deci se returnează 1. Suma divizorilor se poate calcula și mai eficient, parcurgând doar divizorii cu și adunând perechea .