среда, 15 октября 2014 г.

subprograme

NOŢIUNEA DE SUBPROGRAM



Prin subprogram vom înţelege un ansamblu alcătuit din tipuri de date, variabile şi instrucţiuni scrise învederea unei anumite prelucrări (calcule, citiri, scrieri) şi care poate fi utilizat (rulat) doar dacă este apelat de un program sau de alt subprogram. 



Pentru proiectarea unei aplicaţii complexe, este necesară descompunerea problemei care trebuie rezolvată în subprobleme. Pentru fiecare dintre aceste subprobleme se vor scrie module de program mai simple (subprograme), ce vor fi apelate ori de câte ori este nevoie, fie în programul principal, fie în alte subprograme.

În limbajul Pascal subprogramele se împart în două categorii :1. funcţii ;2. proceduri.

Deosebirea dintre ele rezultă din numărul de valori calculate şi returnate programului apelant :1. funcţiile calculează şi returnează o singură valoare, asociată numelui funcţiei ;2. procedurile calculează mai multe valori, eventual nici una.

Subprogramele pot fi :- standard (predefinite în unit-ul SYSTEM);- nestandard (declarate în program de către utilizator).

Subprogramele nestandard trebuie declarate obligatoriu înainte de a fi apelate.
Diagrama de sintaxă pentru declaraţia unui subprogram este :

Pentru funcţie:


Pentru procedură: 


Apelul unui subprogram se face fie în programul principal, fie în alt subprogram.

Diagrama de sintaxă pentru apelul unui subprogram este :
Pentru funcţie: 
Pentru procedură: 


Transferul parametrilor

Parametrii actuali trebuie să corespundă cu parametrii formali ca număr, ordine şi tip.
Transferul parametrilor se face în funcţie de modul de declarare a acestora, şi anume:
- parametrii transmişi prin valoare;
- parametrii transmişi prin adresă(referinţă).

Parametrii transmişi prin valoare sunt parametrii de intrare. În acest caz subprogramul apelant transmite spre subprogram valoarea parametrului actual, iar orice modificare a acestei valori pe parcursul subprogramului, se va pierde la sfârşitul subprogramului, astfel încât la revenire vom avea aceeaşi valoare ca şi la apel.

Parametrii transmişi prin adresă se evidenţiază prin existenţa cuvântului rezervat VAR înaintea declarării parametrului. În acest caz subprogramul apelant transmite spre subprogram valoarea parametrului actual, iar orice modificare a acestei valori pe parcursul subprogramului, se va reţine la sfârşitul subprogramului, astfel încât la revenire vom avea o altă valoare, nu cea de la apel.
Domeniul de vizibilitate a variabilelor

Deoarece un subprogram poate fi definit şi în interiorul unui alt subprogram, putem descrie două modalităţi de structurare a unui program Pascal, folosind :
- dezvoltarea ascendentă
- dezvoltarea descendentă a programului.


Prin domeniul de vizibilitate se înţelege zona din program în care este variabila recunoscută şi se pot efectua cu ea diverse operaţii.
Regulile de vizibilitate sunt :

- o variabilă globală este vizibilă în tot programul din momentul declarării ei ;
- o variabilă locală este recunoscută din momentul declarării ei şi este vizibilă numai în interiorul subprogramului în care a fost definită, precum şi în subprogramele definite în acel ubprogram. În momentul încheierii subprogramului, acea variabilă devine necunoscută.

Pentru cele două modalităţi de strucutare avem, deci, următoarele situaţii :
- pentru dezvoltarea ascendentă, variabilele globale sunt vizibile din momentul declarării lor, în toate subprogramele în care nu există variabile locale cu acelaşi nume ; variabilele locale sunt vizibile, din momentul declarării lor, numai în interiorul subprogramului în care au fost declarate ;
- pentru dezvoltarea descendentă, variabilele globale sunt vizibile din momentul declarării lor, în toate subprogramele în care nu există variabile locale cu acelaşi nume ; variabilele locale sunt vizibile, din momentul declarării lor, atât în interiorul subprogramului în care au fost declarate, cât şi în toate subprogramele definite în subprogramul respectiv.

EXEMPLE DE UTILIZARE A 


FUNCŢIILOR


  1. var i, n, f: integer;
  2.  
  3.   function Fact(var X: Integer)Longint{X este un parametru transmis prin adresa}
  4.   var lFact: Longint{variabila locala}
  5.   begin
  6.     lFact := 1;
  7.  
  8.     for i := 1 to X do
  9.       lFact := lFact * i;
  10.  
  11.     X := -1;
  12.     Fact := lFact; {am asignat rezultatul in numele functiei !}
  13.   end;
  14.  
  15. begin
  16.   Write('Dati N: '); ReadLn(n);
  17.  
  18.   F := Fact(N);
  19.   WriteLn('Factorial de N este egal cu : ', f);
  20.   WriteLn('Noua valoare a lui N este   : ', n);
  21.  
  22.   ReadLn; {asteptam sa fie apasat Enter}
  23. end.



    Recursivitate

    Un lucru important de stiut, legat de subprograme, este ca ele sunt necesare daca vrem sa folosimrecursivitate.

    Un subprogram este recursiv daca in implementarea lui se apeleaza pe el insusi.

    Recursivitatea poate fi un concept destul de greu de inteles, asa ca vom incepe cu un exemplu. Sa vedem cum rescriem functia care calculeaza factorialul, folosind recursivitatea.
    1. function Fact(X: Integer)Longint;
    2. begin
    3.   if X > 1
    4.     then Fact := Fact(X - 1) * X
    5.     else Fact := 1
    6. end;

    Dupa se poate vedea, recursivitatea permite si formularea mai eleganta a solutiei.

    Sa incercam sa intelegem ce se intampla. Factorial de N (notat si N!) este definit ca o inmultire succesiva a numerelor de la 1 la N (1 x 2 x ... x N). Din asta putem vedea ca N! este egal cu (N-1)! x N. La randul lui, (N-1)! = (N-2)! x (N-1) si asta poate continua, pana cand N este egal cu 1. Daca incercam sa calculam 1! folosind aceeasi relatie (adica 1! = 0! x 1) o sa cam dam gres, fiindca 0, inmultit cu orice numar, da 0.

    Pe scurt, daca avem un caz initial caruia ii stim rezultatul, iar restul cazurilor se pot rezolva in functie de cazul initial, problema poate fi rezolvata recursiv. Cazul initial, pe functia de mai sus, este cazul in care X nu este mai mare decat 1 (adica este mai mic sau egal !), acest caz avand rezultatul 1. Pentru orice alta valoare pozitiva a lui X, rezolvarea se face in functie de X-1, pana cand X ajunge in cazul initial.

    Ca urmare, aceste doua relatii de mai sus:
    • N! = (N-1)! x N
    • 1! = 1
    sunt tot ce ne trebuie pentru a ne defini functia recursiva de calculare a factorialului.

    Sa vedem ce se intampla, pas cu pas, pentru N egal cu 3.
    1. Fact(3)
    2. X = 3 -> apelam Fact(2) si inmultim cu 3
    3. X = 2 -> apelam Fact(1) si inmultim cu 2
    4. X = 1 -> 1, se termina functia, revenim in apelul precedent
    5. Fact = 1, care inmultit cu 2 este egal cu 2, revenim in apelul precedent
    6. Fact = 2, care inmultit cu 3 este egal cu 6, se termina functia si revenim in locul unde a fost initial apelata functia.
     Deci, daca avem o problema care poate fi exprimata prin subprobleme ale ei si putem gasi o conditie de terminare a apelurilor recursive (in acest caz, X = 1), problema poate fi rezolvata printr-un subprogram recursiv.

    Atentie: Apelurile recursive consuma relativ multa memorie. Daca o problema poate fi rezolvata iterativ (fara apeluri recursive), e de preferat (in general) ca problema sa fie rezolvata iterativ. O lista prea lunga de apeluri recursive poate genera "stack overflow" si bloca programul / calculatorul.

    Pentru edificare, atasez acelasi program care calculeaza factorialul, modificat pentru a afisa ce se intampla. Sper sa va fie de ajutor, impreuna cu explicatiile de mai sus.

    Codul sursa al programului:
    1. uses crt;
    2.  
    3. const lin = 9;
    4.       line = '-----------------------------------------------------------------';
    5.       Wait = 1000;
    6.  
    7. var i, n, f, lFact, test: longint;
    8.  
    9.   function Fact(X: Integer)Longint{X este un parametru, transmis prin valoare}
    10.   begin
    11.     if X > 1
    12.       then
    13.         begin
    14.           WriteLn('Fact(', X, ') = Fact(', X - 1 ,') * ', X);
    15.           WriteLn(line);
    16.           Delay(Wait);
    17.  
    18.           Fact := Fact(X - 1) * X;
    19.           Delay(Wait);
    20.  
    21.           TextColor(LightGreen);
    22.           if Test > 0 then
    23.             Test := Test * X;
    24.           GotoXY(40(N - X) * 2 + lin);
    25.           Write('Fact(', X,') = ', Test);
    26.         end
    27.       else
    28.         begin
    29.           TextColor(LightRed);
    30.           WriteLn('X = 1                                  ' +
    31.                   'Fact(1) = 1, caz initial !');
    32.           TextColor(LightGray);
    33.           WriteLn(line);
    34.           Delay(Wait);
    35.           WriteLn('Functia s-a terminat, revenim din apel ^^^');
    36.           Delay(Wait);
    37.           Fact := 1;
    38.           Test := 1;
    39.         end;
    40.   end;
    41.  
    42. begin
    43.   test := 0;
    44.   repeat {verificam daca 1 <= N <= 6}
    45.     ClrScr;
    46.     Write('Dati N (maxim 6): '); ReadLn(n);
    47.   until (N > 0) and (N < 7);
    48.   ClrScr;
    49.  
    50.   TextColor(LightRed);
    51.   GotoXY(Length(line) div 2 - 16 ,2);
    52.   WriteLn('Calculul factorialului - recursiv');
    53.   WriteLn(line);
    54.   WriteLn;
    55.   TextColor(LightGray);
    56.   WriteLn('Se apeleaza Fact(', N,') ...');
    57.   Delay(Wait);
    58.  
    59.   TextColor(lightGray);
    60.   GotoXY(1, lin);
    61.   F := Fact(N);
    62.  
    63.   Delay(Wait);
    64.   GotoXY(1, N * 2 + lin + 3);
    65.   TextColor(LightGray);
    66.  
    67.   Write('Fact(', N, ') este egal cu : ');
    68.   TextColor(LightGreen);
    69.   WriteLn(f);
    70.   TextColor(LightGray);
    71.   Write('... apasa orice tasta pentru a termina programul ...');
    72.  
    73.   While Keypressed Do
    74.     Readkey; {daca s-a apasat o tasta in timp ce programul rula,
    75.               va astepta totusi apasarea urmatoarei taste}
    76.   ReadKey;
    77. end.



      Vizibilitatea variabilelor

      Dupa cum am explicat mai sus, se pot declara variabile in interiorul subprogramelor, chiar si cu nume de variabile care exista deja definite in programul principal. Problema care se ridica este urmatoarea : de unde stie compilatorul (si implicit, programatorul) care variabila va fi folosita in anumite zone ale programului ?

      Nu trebuie sa va speriati, este foarte simplu si usor de inteles.

      Variabilele globale ale programului sunt vizibile oriunde in program. Daca un subprogram isi defineste o variabila cu acelasi nume, atunci variabila locala e prioritara in acel subprogram (si in posibilele subprograme ale subprogramului !).

      1. var i, n, f, lFact: integer;
      2.  
      3.   function Fact(X: Integer)Longint{X este un parametru, transmis prin valoare}
      4.   var lFact: Longint{variabila locala}
      5.   begin
      6.     lFact := 1;
      7.     WriteLn('lFact local  = ', lFact);
      8.  
      9.     for i := 1 to X do
      10.       lFact := lFact * i;
      11.  
      12.     X := -1;
      13.     Fact := lFact; {am asignat rezultatul in numele functiei !}
      14.   end;
      15.  
      16. begin
      17.   Write('Dati N: '); ReadLn(n);
      18.  
      19.   lFact := 157;
      20.   WriteLn('lFact global = ', lFact);
      21.  
      22.   F := Fact(N);
      23.   WriteLn('Factorial de N este egal cu : ', f);
      24.  
      25.   ReadLn; {asteptam sa fie apasat Enter}
      26. end.
      Acest program va afisa urmatoarele linii (pentru N egal cu 5):
      1. Dati N: 5
      2. lFact global = 157
      3. lFact local  = 1
      4. Factorial de N este egal cu : 120

Комментариев нет:

Отправить комментарий