>> Inapoi <<

========
Capitolul 4
========

=========================
Functii si programare structurata
=========================

Programarea structurata este o problema ce rezolva strategia si metodologia programarii si are
urmatoarele principii:

1. Structurile de control trebuie sa fie cat se poate de simple;
2. Constructia unui program trebuie sa fie descrisa top-down.

Descrierea top-down se refera la descompunerea problemei noastre in 
subprobleme. De obicei, aceste subprobleme sunt usor de descris.


---------------------
Apelul functiilor
---------------------

Un program este compus din una sau mai multe functii, printre care si "main()".
Intotdeauna executia unui program incepe cu "main()". Cand o functie este 
apelata (sau invocata) atunci controlul programului este pasat functiei 
apelate. Dupa ce aceasta isi termina executia, atunci se paseaza inapoi 
controlul catre program.

Codul C care descrie ce face o functie se numeste "definitia functiei". 
Aceasta are urmatoarea forma generala:

        tip  nume_functie (lista_parametri)
         {
          declaratii
          instructiuni
         }
         
Primul rand se numeste "header-ul" (antetul) functiei, iar ceea ce este inclus
intre acolade se numeste corpul functiei. Daca in antet nu precizam parametri,
atunci se va scrie "void" (cuvant rezervat pentru lista vida). Daca functia nu
intoarce nici o valoare, atunci se va scrie ca tip intors tot "void". Tipul 
intors de functie este cel precizat in "return" (ce va fi indata explicat). 
Parametrii din antetul functiei sunt dati printr-o lista cu argumente separate
prin virgula. Aceste argumente sunt date de tipul argumentului urmat de un 
identificator ce apartine acelui tip. Se mai spune ca acel identificator este 
"parametru formal".

-----------
Exemplu:
-----------
#include 

void tipareste_mesaj(int k)
 {
  int i;
  printf("Iti urez:\n");
  for (i = 0; i < k; ++i)
    printf("  O zi buna ! \n");
 }

main()
 {
  int n;
  printf("Dati un numar natural mic: ");
  scanf("%d", &n);
  tipareste_mesaj(n);
 }
 

----------------------------
Instructiunea "return" 
----------------------------

Instructiunea "return" este folosita pentru doua scopuri. Cand se executa o 
instructiune "return", controlul programului este pasat inapoi programului 
apelant. In plus, daca exista o expresie dupa acest "return", atunci se va 
returna valoarea acestei expresii. 
 Instructiunea "return" poate avea formele:

        return;
          sau
        return expresie;
        
-----------
Exemplu: Minimul a doi intregi.
-----------     

#include 

int min(int x, int y)
 {
  if (x < y)
    return x;
  else
    return y
 }
 
main()
 {
  int j, k, m;
  
  printf("Dati doi intregi: ");
  scanf("%d%d", &j, &k);
  m = min(j, k);
  printf("\n%d este minimul dintre %d si %d.\n", m, j, k);
 }
 

------------------------------
Prototipurile functiilor
------------------------------

In C, apelul unei functii poate apare inaintea declararii ei. Functia poate fi
definita mai tarziu in acelasi fisier, sau in alt fisier sau dintr-o biblioteca
standard. In ANSI C, prototipul functiei remediaza problema punand la 
dispozitie numarul si tipul argumentelor functiei. Prototipul specifica, de 
asemenea, si tipul returnat de functie. Sintaxa prototipului unei functii este:

        tip  nume_functie  (lista_tipuri_parametri);
        
In lista de parametri putem specifica chiar si parametrul, dar asta este 
optional. Daca functia nu are parametri, atunci se foloseste "void". 

-----------
Exemplu: Reluam un exemplu precedent.
-----------
#include 

main()
 {
  int n;
  void tipareste_mesaj(int);
  
  printf("Dati un numar natural mic: ");
  scanf("%d", &n);
  tipareste_mesaj(n);
 }

void tipareste_mesaj(k)
 {
  int i;
  
  printf("Iti urez:\n");
  for (i = 0; i < k; ++i)
    printf("  O zi buna ! \n");
 }

Prototipul unei functii poate fi plasat in corpul altei functii, sau de regula,
se scriu la inceputul programelor dupa directivele 
#include si #define.


----------------------------
Descriere "top-down"
----------------------------

Presupunem ca avem de citit cativa intregi si trebuie sa-i afisam in ordine pe
coloane (in capatul de sus al coloanelor trebuie sa scriem numele campului), sa
le afisam suma lor partiala, minimul si maximul lor. Pentru scrierea unui 
program C ce face acest lucru, vom utiliza proiectarea (descrierea) "top-down".

Astfel, descompunem problema in urmatoarele subprobleme:

1. Un antet pentru problema data;
2. Scrierea campurilor;
3. Citirea si scrierea lor pe coloane.

Toti acesti trei pasi vor fi descrisi in cate o functie ce se apeleaza din 
"main()". Obtinem, un prim cod:

#include 

main()
 {
  void tipareste_antet(void);
  void scrie_campurile(void);
  void citeste_scrie_coloanele(void);
  
  tipareste_antet();
  scrie_campurile();
  citeste_scrie_coloanele();
 }

Aceasta reprezinta intr-un mod foarte simplu descrierea "top-down". Daca o 
problema este prea grea, atunci o descompunem in subprobleme, si apoi le 
rezolvam pe acestea. Beneficiul suplimentar al acestei metode este claritatea 
sa.

void tipareste_antet(void)
 {
  printf("\n%s%s%s\n",
         "**************************************************\n",
         "*    Calculul sumelor, minimului si maximului    *\n", 
         "**************************************************\n");         
 }
 
Functia ce foloseste la scrierea campurilor este la fel usor de scris:

void scrie_campurile(void)
 {
  printf("%5s%12s%12s%12s%12s\n\n",
         "Numar", "Articol", "Suma", "Minimul", "Maximul");
 }

Urmeaza apoi functia ce serveste la scrierea inregistrarilor referitoare la 
campurile discutate mai sus:

void citeste_scrie_coloanele(void)
 {
  int contor = 0, articol, suma, minim, maxim;
  int min(int, int), max(int, int);
  
  if (scanf("%d", &articol) == 1)
   {
    ++contor;
    suma = minim = maxim = articol;
    printf("%5d%12d%12d%12d%12d\n\n",
           contor, articol, suma, minim, maxim);
    while (scanf("%d", &articol) == 1)
     {
      ++contor;
      suma += articol;
      minim = min(articol, minim);
      maxim = max(articol, maxim);
      printf("%5d%12d%12d%12d%12d\n\n",
             contor, articol, suma, minim, maxim);
     }     
   }
  else
    printf("Nici o data nu a fost citita.\n\n"); 
 }
 
Daca datele se introduc de la tastatura, atunci tabelul se va afisa "intrerupt"
de citirile ce au loc de la tastatura. Astfel, se prefera citirea dintr-un 
fisier extern. Presupunem ca fisierul nostru executabil (asociat fisierului 
sursa scris in C) se numeste "numere.exe" si am creat un fisier numit 
"fisier.int" ce contine urmatoarele numere: 

                19  23  -7  29  -11  17
        
Dand comanda 

                numere < fisier.int
                
vom obtine un tabel ce contine toate datele dorite.


-------------------------------------
Invocare si apel prin valoare
-------------------------------------

O functie este invocata prin scrierea numelui sau impreuna cu lista sa de 
argumente intre paranteze. De obicei, numarul si tipul acestor argumente se 
"potriveste" cu parametrii din lista de parametri prezenti in definitia 
functiei. Toate argumentele sunt apelate prin valoare ("call-by-value"). Asta 
inseamna ca fiecare argument este evaluat si valoarea sa este folosita ca 
valoare pentru parametrul formal corespunzator. De aceea, daca o variabila 
(argument) este folosita la transmiterea unei valori, atunci valoarea ei 
nu se schimba. 

-----------
Exemplu:
-----------
#include 

main()
 {
  int n=3, suma, calculeaza_suma(int);
  
  printf("%d\n", n);         /* se va scrie 3 */
  suma = calculeaza_suma(n);
  printf("%d\n", n);         /* se va scrie 3 */
  printf("%d\n", suma);      /* se va scrie 6 */
 }

int calculeaza_suma(int n)   /* suma numerelor de la 1 la n */
 {
  int suma = 0;
  for ( ; n > 0; --n)        /* n se schimba aici, dar nu si in main() */
    sum += n;
  printf("%d\n", n);         /* se va scrie 0 */
  return suma;
 }  

Chiar daca n este trimis ca argument in functia "calculeaza_suma()" si valoarea lui n se 
modifica in aceasta functie, valoarea sa din mediul apelant ramane neschimbata. 
Vom vedea mai tarziu cum se poate simula apelul prin adresa ("call-by-reference").


----------------------------------------------
Deosebirea dintre "return" si "exit"
----------------------------------------------

Exista doua procedee de a returna o valoare.

        return expresie   si    exit(expresie)
        
Daca se folosesc in "main()", atunci acestea sunt echivalente, dar in orice 
alta functie efectul lor este diferit (in ANSI C, functia "main()" intoarce o 
valoare de tip int). Un apel al lui exit() in orice alta functie va implica 
terminarea executiei programului si returnarea valorii catre mediul apelant 
(sistemul de operare sau mediul de programare C). Valoarea returnata se numeste
stare de iesire ("exit status"). Prin conventie, starea de iesire zero indica 
terminare cu succes, pe cand iesire cu un numar diferit de zero indica o 
situatie anormala.


-----------------------------------------------
Exercitii propuse spre implementare
-----------------------------------------------

1. Folosind functiile "rand()", "min(,)" si "max(,)", sa se genereze  n numere
naturale si sa se afiseze minimul si maximul dintre acestea.

2. (Jocul cap-pajura, simulare Monte-Carlo) 
  Presupunem ca dispunem de o moneda ideala (nemasluita). Doi jucatori arunca 
 cu moneda dupa urmatoarele reguli:
  1.a. Se fac un numar total de n aruncari;
  1.b. Primul jucator arunca moneda si celalalt spune "cap" sau        "pajura";
 1.c. Daca acesta "ghiceste" ce va pica moneda, atunci se inverseaza jucatorii 
  (adica arunca al doilea si primul incearca sa ghiceasca);
  1.d. La sfarsit, trebuie afisat scorul (si procentul de castig al fiecaruia).

3. (Conjectura lui Goldbach)
  Orice numar par mai mare decat 2 se poate scrie ca suma a doua numere prime. 
 Scrieti un program C care verifica aceasta conjunctura pentru numere situate 
 intre m si n. De exemplu, daca m=700 si n=1100, atunci afisati:

        700 = 17 + 683
        702 = 11 + 691
        704 =  3 + 701
            ...
       1098 =  5 + 1093
       1100 =  3 + 1097
           
  Generalizare: Scrieti toate combinatiile posibile de adunare a doua numere 
 prime egal cu un numar dat.