Funcții și proceduri


Recursia

Suntem pe stradă, într-un oraş străin şi căutăm cel mai apropiat loc unde putem consulta poşta electronică.
1. V-om întreba persoanele întâlnite pe drum şi presupunând că acestea ştiu să ne răspundă apar situaţiile :
a)   drumul este simplu si urmând indicaţiile, putem ajunge direct;
b)  drumul este complicat, parcurgem o porţiune din drum, conform explicaţiilor primite apoi întrebăm din nou
 ne confruntăm cu o nouă versiune a problemei iniţiale, dar, de data aceasta, dintr-un loc mai apropiat de destinaţia noastră.
Istorioara de mai sus rezumă esenţa repetiţiei unui anumit proces, punând în evidenţă şi pericolul ciclării acesteia
(ciclare = repetiţie infinită) - profesorul defineşte noţiunea de proces recursiv.

Expunem regulile de formare a recursiei:
- trebuie să fie formată din cazuri elementare care se rezolvă direct,
- cazuri care nu se rezolvă direct, însă procesul de calcul în mod obligatoriu progresează spre un caz elementar.
Situaţie de problemă
 "Cînd un proces este recursiv? " pe baza studierii enunţului :
" Intr-o fabrică, un robot trebuie să preia piese din linia de fabricaţie şi să le aşeze pe paleţi"
- pentru  a  dirija  activitatea   de  învăţare   spre   actul descoperirii profesorul apelează la întrebări ajutătoare:

1. După ce se umple un paiet , robotul trebuie să înceapă umplerea altuia?
2. Există mai multe procese recursive în acest caz?
3. Dacă da, care sunt acestea?

 Pe baza celor enunţate înseamnă că avem de fapt două procese recursive, aflate unul în celălalt astfel spus imbricate. Un proces recursiv este acela prin care robotul trebuie să umple un paiet iar al doilea este acel  că robotul trebuie să umple un număr de paleţi, aţâţi cât sunt necesari pentru a prelua piesele din fluxul de fabricaţie
Ex: - prin antiteză există şi procese recursive infinite - procesul prin care robotul preia piese din fluxul de fabricaţie şi le aşează pe paleţi ( singurele cauze care pot genera încheierea procesului fiind oprirea robotului de către factorul uman sau prin defectare).

  •  definiţiile recursive date în exemplu au toate aceeaşi structuгă conţinînd două părţi marcate cu (0) şi (1). - prima ramură (0) - este condiţia de oprire - execuţie pгin autoapel.
  • a doua ramură (1) - descrierea operaţiilor care se fac la un pas  oarecare  al algoritmului (proces inductiv) - se va face analogic cu metoda inducţiei matematice.
Conceperea algoritmilor recursivi:
? Definiţi algoritmul şi scrieţi un program pentru calculului n! = 1×2×3……….n într-o manieră recursivă:
?Care este condiţia de oprire?
?Cum definim n
?Care sunt parametrii destinaţi calculului n!?
?De cîte ori se apelează funcţia ce calculează n!  pentru n=3?
?Ce eroare apare la depăşirea limitei superioare a  stivei?

Răspuns:
Se înmulţesc toate numerele de la 1 la n.
!Din definiţie avem că 0! = 1 
!Toate cazurile se reduc la cazul elementar 0! (n-1, n-2 … )  

Exemplul programului: 
 Program factorial;
Var n:byte;
Function Fact(n:byte):longint;
Var f:longint;
Begin
Write('apel pentru n=',n);
If n=0 then f:=1
        Else f:=n*fact(n-1);
Fact:=f;
Writeln('Revenire din apelul pentru n=',n,', ',n,'!=',f)
End;
Begin
Write('n='); Readln(n);
Fact(n);
End.

Pe diagrama respectivă se explică lucrul recursiei şi se face analogie cu iteraţia.

Recursia se defineşte ca o situaţie, în care un subprogram se autoapelează, fie direct, fie prin intermediul altei funcţii sau proceduri. Subprogramul care se autoapelează se numeşte recursiv.
1.      Numerele naturale:
a)      1 este număr natural;
b)      Numărul întreg ce urmează imediat după un număr natural este un număr natural.
2.      Funcţia factorial(n) pentru numere întregi nenegative:
a)      factorial(0)=1,
b)      factorial(n)=n*factorial(n-1), dacă n>1.
Recursia este utilă pentru programarea unor calcule repetitive. Repetiţia este asigurată prin execuţia unui subprogram, care conţine un apel la el însuşi: cînd execuţia ajunge la acest apel, este declanşată o nouă execuţie ş.a.m.d.
Orice subprogram recursiv  trebuie să includă condiţii de oprire a procesului repetitiv.
La orice apel de subprogram în memoria calculatorului vor fi depuse următoarele informaţii:
·         Valorile curente ale parametrilor transmişi prin valoare;
·         Locaţiile (adresele) parametrilor – variabilă;
·         Adresa de retur, adică adresa instrucţiunii ce urmează după apel.

Structuri dinamice de date



Variabile statice şi variabile dinamice.

Variabilele pot fi statice sau dinamice. Cele statice au o zonă de memorie bine definită. Structura, locul, tipul acestei zone nu poate fi schimbată în procesul execuţiei programului. Accesul la variabilele statice se face direct, după nume. Structurile statice de date au un număr fix de elemente, care nu poate fi modificat. Alocarea memoriei se face într-un spaţiu strict determinat al memoriei, care permite adresarea directă.

            O altă categorie de variabile, accesibile în limbajul Pascal este cea dinamică. Pentru acest tip de variabile poate fi modificat volumul de memorie rezervat, ceea ce face mai flexibilă alocarea memoriei în procesul de lucru al programului. Structurile dinamice pot acumula elemente în procesul de funcţionare al programului, sau pot lichida elementele ce au devenit neutilizabile. Accesul la aceste variabile şi declararea lor se face în mod diferit de cel al variabilelor statice. Accesarea (definirea) este indirectă, prin intermediul unui tip mediator de variabile – tipul referinţă.

            Variabile de tip referinţă conţin referiri (adresări indirecte) la variabilele dinamice. Referirea se realizează prin memorarea în variabila referinţă a adresei unde e stocată variabila dinamică.

            Pentru variabilele tip referinţă se alocă un spaţiu de memorie de 4 octeţi, care conţin adresa variabilei dinamice. Memoria pentru variabilele dinamice se alocă dintr-o zonă specială, care foloseşte adresarea indirectă, numită heap. Această zonă  este diferită de zona pentru variabilele statice.
            Variabilele dinamice ocupă un spaţiu de memorie în corespundere cu tipul lor: Integer, real, string etc.



Definirea tipului referinţă.
Pentru a defini un tip referinţă vom folosi secţiunea Type în blocul de declarare a variabilelor. Procedura de definire este următoarea:

type <tip_referinţă> = ^<tip_variabilă_dinamică>;


^ - se citeşte ca “pointer la…” sau “adresare la…“

Mulţimea valorilor variabilelor tip referinţă este formată dintr-o mulţime de adrese, fiecare din ele identificînd o variabilă dinamică. Mulţimea de valori mai e completată prin valoarea Nil, care nu conţine nici o adresă.

Schema de lucru a variabilelor referinţă:

  

Exemplu:

type pointReal = ^Real;
var ar, br, cr : pointReal;
Exemplu

type pointReal = ^Real;
var ar, br, cr : pointReal;
            Begin
                        New (ar); New (br); New (cr);
                        Readln (ar^, br^);
            cr^ := ar^+ br^;
                        Writeln (cr^);
            End.


            Variabila tip_referinţă păstrează adresa adresei variabilei dinamice. Pentru a putea utiliza variabilele dinamice în program ele trebuie iniţializate prin procedura NEW.
Ca variabilele dinamice pot fi declarate şi structuri de tip Record, iar tipul lor poate fi definit (declarat) mai tîrziu, dat în acceaşi secţiune de declaraţii type:

type    num_complex = ^complex;
            complex = record
                                   re,
                                   im : Real
            end;
var
            num1, num2 : num_complex;
            num3 : ^complex;
            simbol : ^char;




Liste unidirectionale

Logica creării unei liste (este cea mai simplă structură) e următoarea: pentru fiecare element al listei vom avea nevoie de spaţiu pentru el însuşi şi de spaţiu pentru adresa elementului următor al listei.

Exemplu:

type lista = ^element;
element = record
                        a:                    integer,
                        urmator :        lista
                   end;
Var
            lis : lista;

Schema creării listei:

acesta este lis^                                             acesta este lis^.urmator
      a           urmator                                               a           urmator


un articol
    (primul din lista lis)                                


Aşa dar, memorarea şi atribuirea valorilor se realizează după schema:

a.     declararea variavilei dinamice
b.     alocarea memoriei pentru variabila dinamică prin procedura                       
New (nume variabilă dinamică)
c.     atribuirea valorii numerice prin unul din procedeele standard.

Eliberarea memoriei ocupate de variabila dinamică se va realiza prin procedura                 
Dispose (nume variabilă dinamică)

type lista = ^element;
element = record
             a:           integer;     {elementul propriu zis}
             urmator :    lista        {adresa următorului element al listei}
              end;
Var
       lisinit, liscurent : lista;
       raspuns : char;

begin
       lisinit := Nil;   {lista nu contine nici un element, ea se initializeaza}
       repeat
     begin
       New(liscurent); {un nou element in lista }
       Writeln ('Elementul curent');
       readln(liscurent^.a);   {dam valori elemetului listei}
             liscurent^.urmator := lisinit;
{indicam urmatoarea adresă, acea a primului element din listă, transformîndu-l în al doilea. De fapt plasăm elementul la începutul listei, împingîndu-le pe toate celelalte spre dreapta}
        lisinit:=liscurent; {Lista nou formată o memorăm în variabila listinit}
     writeln ('Continuati? [D - Da, N / Nu]'); readln(raspuns);
     end;
until (raspuns='N') or (raspuns='n');
while  liscurent <> Nil do
begin
writeln (liscurent^.a);
liscurent:= liscurent^.urmator;
end;
end.


 
isinit Astfel prgramul prezentat mai sus crează mai întîi o listă dinamică vidă, apoi adaugă elemente în stinga ei pînă nu intrerupem operatia. Intrucît la fiecare iteraţie noi revenim la începutul listei, realizăm parcurgerea ei pentru a tipări elementele.




Arbori


Numim arbori,un graf conex fara cicluri

TEOREMA:

1.Un arbore cu n varfuri are n-1 muchii;
2.Urmatoarele afirmatii sunt echivalente:
      - G este arbore
      - G este un graf conex minimal cu aceasta proprietate,adica prin eliminarea unei muchii se obtine un graf neconex;
       - G este un graf fara cicluri maximal cu aceasta proprietate,adica prin adaugarea unei muchii se obtine cel putin un ciclu;

Vor aparea urmatoarele notiuni:
-          radacina(varful arborelui) este nodul in care nu intra niciun arc;
-          parintii(predecesorii) reprezinta nodurile din care ies arce;
-          fiii (succesorii) sunt nodurile legate de parinti;
-          frunzele(noduri terminale) sunt nodurile situate pe ultimul nivel sau care nu au descendenti;
-          nivelele arborelui sunt stratificari(trepte) ale arborelui stiind ca radacina se situeaza pe nivelul 1;
-          inaltimea arborelui reprezinta lungimea celui mai lung lant ce pleaca din radacina si ajunge in frunze;

Reprezentarea arborelui
Se va folosi un vector “tata” in care in tata de i se va retine parintele nodului i.
Pentru radacina se retine 0.
Descendentii directi ai unui nod reprezinta fiii nodului situati pe nivelul urmator.
Toti descendentii unui nod reprezinta toti fiii nodului respective pana la frunza.




Arborele binar reprezinta arboreal pentru care toate nodurile cu exceptia frunzelor au cel mult 2 descendenti.

Reprezentarea arborilor binari

1.Se specifica radacina si se vor folosi 2 vectori si d in care se va specifica in s[i] sid[i],nodul successor stang,respectiv nodul succesor drept.




2.Se vor folosi 2 vectori tata[i] si p[i]. In tata[i] se va retine tatal nodului i, iar inp[i] se va retine tipul succesorului:  - pentru radacina;
                               -  -1  pentru subarborele stang
                               -  pentru subarborele drept
                   






    
                                     

                                                                                         

Tipuri de date structurate



Siruri de caractere-string

D Un sir de caractere reprezinta o succesiune de caractere cuprinsa intre apostrofuri. Aceasta succesiune poate fi alcatuita din:
-litere mici;
-litere mari;
-cifre;
-delimitatori(., ;);
-caractere speciale(# @);
Un sir de caractere poate fi memorat intr-o variabila de tipSTRING.
Declararea unei astfel de variabile se poate realize fie prin specificarea dimensiunii maxime a sirului , fie nu.

1.      var nume:string[25];  2. var nume: string;

    nume:string[nr]; 1<=nr<=255;

   Daca la declararea unei variabile este specificata dimensiunea maxima a sirului de caractere , atunci compilatorul va aloca un numar nr de caractere pentru aceasta variabila .
   Daca dimensiunea nu este specificata atunci compilatorul va aloca dimensiunea maxima , adica 255 de caractere.
   Unui sir i se poate asocial o valoare prin doua modalitati:
a)prin atribuirea directa a unei valori folosind operatorul de atribuire(:=);
b)prin procedura (read, readln);

!!!Observatie!!!
  Daca valoarea ce i se atribuie unui sir de caractere depaseste lungimea maxima a sirului , atunci sirul de caractere va primi o valoare trunchiata din valoarea initiala egala cu lungimea maxima a sa.

Exemple:
a) s:string[3]; s:=’abcde’=>s=’abc’;
b) s:string; s:=’abcde’=>s=’abcde’;
c) s=’‘-stringul de lungime 0;
d) s=’ ‘-sir de caractere de lungime 1.
Operatii pe siruri de caractere

1)Concatenarea a doua sau mai multe siruri de caractere.
Se realizeaza folosind operatorul ”+”.
s:string;
s:=’ab’+’ ‘+’cde’;
ð        s=’ab cde’
2)Compararea a doua caractere precum si compararea a doua siruri de caractere se realizeaza folosind operatorii relationali: =, <, <=, >, >=, <>.
Compararea a doua caractere se realizeaza comparand codurile Asci ale celor doua caractere.
Compararea a doua siruri de caractere se realizeaza prin compararea lexicografica , reducandu-se la compararea a doua caractere.
3)Parcurgerea unui sir de caractere
length(sir)-returneaza lungimea sirului specificat.
sir:string;
Valoarea returnata de aceasta functie este o valoare de tip byte.
length(‘’)=0-sirul vid.
s:string ;
s[i]-caracterul de pe pozitia i;
for i:= 1 to length(s) do
        <prelucreaza s[i]>;
!!!Observatii!!!
1)Relatia de legatura intre caracterele mari si caracterele mici este urmatoarea:
CA(mare)+32=CA(mic); CA-codul Asci;
2)ord , chr –doua functii predefinite specifice caracterelor;
ORD(un character)=codul Asci al caracterului;
CHR(un numar)=caracterul corespunzator codului Asci.

Functii predefinite pe stringuri

1)POS(sir1,sir2);
2)COPY(sir,poz,nr);
3)LENGTH(sir);
4)CONCAT(c1,c2,…,cn);

1)    Functia POS verifica daca sirul 1 se situeaza in sirul 2 si returneaza pozitia de inceput in sirul 2 daca exista sau 0 daca sirul 1 nu se afla in sirul 2.
2)    Functia COPY extrage un subsir al sirului specificat, incepand din pozitia poz cu un numar nr de caractere.
3)    Functia LENGTH returneaza lungimea unui sir de caractere.
4)    Functia CONCAT lipeste mai multe siruri c1,c2,…,cn.

!!!Observatii!!!
1)    Daca poz este mai mare decat length(sir) atunci sirul rezultat va fi sirul vid.
2)    Daca nr este mai mare decat length(sir)-poz+1 atunci subsirul obtinut va contine numai length(sir)-poz+1 caractere.

|----------|---------------------|
1              poz                           length(sir)
               |---------------------|
                     Length(sir)-poz+1





Proceduri predefinite in stringuri

1)DELETE(sir,poz,nr)
Sterge din sir un sir incepand cu pozitia poz un numar nr de caractere.
!!!Observatie!!!
1)    Daca poz>length(sir) atunci nu se sterge nimic.
2)    Daca nr>length(sir)-poz+1 , atunci se vor sterge length(sir)-poz+1 caractere.
Exemple
S=’casa’;
1)delete(S,2,2)->S=’ca’;
2)delete(S,7,3)->S=’casa’;
3)delete(S,3,7)->S=’ca’.

2)INSERT(subsir, sir, poz)
Insereaza subsirul dat in sirul sir incepand din pozitia poz.
!!!Observatii!!!
1)    Daca pozitia este mai mare decat length(sir) , atunci subsirul se insereaza la sfarsitul sirului .
subsir=’rr’; sir=’casa’;
insert(subsir,sir,30)->sir=’casarr’;
insert(subsir,sir,2)->sir=crrasa’.
2)    Daca lungimea sirului + lungimea subsirului depaseste lungimea de definitie a sirului “sir”, atunci lungimea sirului se pastreaza la lungimea de definitie (trunk(int)) character de dupa lungime.
   sir=’casa’; subsir=’rr’; sir:string[4];
   insert(subsir,sir,2)->sir=’crra’.
 3)STR(numar,sir)
 Transforma variabila “numar” intr-un sir de caractere.
   12->’12’.
4)VAL(sir, numar, eroare)
Incearca sa transforme sirul de caractere “sir” intr-un numar real. Daca reuseste atunci parametrul eroare va avea valoarea 0, daca nu reuseste valoarea parametrului “numar”=0, “eroare” va avea pozitia de unde incepe nereusita.
Val(‘12.3’, numar, eroare)-> eroare=0
                             ->numar=12.3
Val(’12,3’,numar,eroare)->eroare=3
                           ->numar=0.




SET DE PROBLME
1)  Sa se afiseze toate prefixele unui cuvant.
2)Sa se afiseze toate sufixele unui cuvant.
3)Sa se elimine toate spatiile dintr-un text.
4)Verificati daca un cuvant este palindrome.5)Afisati cate vocale contine un text.
6)Se da un text in care cuvintele sunt separate prin spatii . Sa se afiseze pe cate un rand cuvintele textului.


Program problema_1;

Var cuv:string;
     n,i:integer;
Begin
write(‘Cuvantul este:’);
readln(cuv);
n:=length(cuv);
for i:= 1 to n do
                  writeln(copy(cuv,1,i);
readln;
END.



Program problema_2;

Var cuv:string;
     n,i:integer;
Begin
write(‘Cuvantul este:’);
readln(cuv);
n:=length(cuv);
for i:= n downto 1 do
                       writeln(copy(cuv,i,n);
readln;
END.




Program problema_3;

Var cuv:string
     i,n:integer;
Begin
write(‘Cuvantul este:’);
readln(cuv);
n:=length(cuv);
for i:= 1 to n do
     if cuv[i]=’ ‘then begin
                       delete(cuv,i,1);
                        n:=n-1;
                       end;
for i:= 1 to n do
    write(cuv[i]);
readln;
END.



Program problema_4;
Var cuv:string;
     i,n:integer;
     OK:Boolean;
Begin
write(‘Cuvantul este:’);
readln(cuv);
n:=length(cuv);
OK:= true;
for i:= 1 to n div 2 do
   if cuv[i]<>cuv[n-i+1] then OK:=false;
if OK then writeln(‘Cuvantul este palindrom’)
       else writeln(‘Cuvantul nu este palindrom’);
readln;
END>

Program problema_5;
Var text:string;
     i,n,nr:integer;
Begin
write(‘Textul este:’);
readln(text);
n:=length(text);
nr:=0;
for i:= 1 to n do
if (text[i] in [‘A’,’E’,’I’,’O’,’U’]) or (text[i] in[‘a’,’e’,’i’,’o’,’u’]) then inc(nr);
writeln(‘sunt’,nr,’vocale’);
readln;
END.




Program problema_6;

Var t:string;
     n,i:integer;
     cuv:string;
Begin
Write(‘Dati textul:’);
Readln(t);
N:=length(t);
I:=1;
While i<= n do
        Begin
           While (t[i] in[‘ ‘,’,’,’.’,’?’,’!’,’:’,’;’]) and (i<=n) do
                Inc(i);
            Cuv:=’  ‘;
            While not(t[i] in[‘ ‘,’,’,’.’,’?’,’!’,’:’,’;’]) and (i<=n) do
                  Begin
                    Cuv:=cuv+t[i];
                    Inc(i);
                   End;
            Writeln(cuv);
         End;
Readln;
End.




Fişiere
Un fişier este o structură de date cu componente numite înregistrari ce pot avea dimensiuni fixe sau variabile, in cel de-al doilea trebuind sa existe nişte marcatori care să le delimiteze.
  Fisierele se pot clasifica dupa mai multe criterii:
-         dupa accesul la componente:
·        fişiere cu acces secvenţial – in care prelucrarea datelor se face în ordinea în care acestea au fost citite;
·        fişiere cu acces direct – in care accesul la componente se face în mod direct,fara a se ţine cont de ordinea introducerii lor;
-         după conţinutul fişierelor:
·        fişiere text,datele fiind considerate caractere scrise secvenţial,unele după altele,pe linii,asemănător modului natural de scriere;
·       fişiere binare,ce pot conţine date de tipuri diferite,în acest cazfiecare componentă este considerată 1 octet;
Operaţii cu fişiere text
   Un fişier text este un fişier ale cărui componente sunt caractere scrise secvenţial pe linii.Pentru a putea lucra cu fişiere text trebuie ca într-un program Pascal să avem definită o variabilă care să facă legătura cu un fişier text memorat pe un suport extern(HDD).O astfel de variabilă se declară:
    nume_variabilă:text;
Exemplu:  f:text;
 Legătura dintre o variabilă şi fişierul extern se face cu ajutorul procedurii assign:
   assign(variabilă_fişier,'nume_fişier_extern');
Exemplu:   assign(f,'fişier.txt');

Alte proceduri de lucru cu fişiere text sunt:
·        rewrite – este o procedură ce permite creearea unui fişier text şi deschiderea acestuia pentru scrierea în el.Ea se poate utiliza astfel: rewrite(f); ,unde f este variabila de tip fişier text asociată cu un fişier text extern;
·        reset – procedură ce permite deschiderea unui fişier pentru citirea din acesta;
·        append – procedură ce permite scrierea la sfârşitul fişierului text,adică adăugarea unei noi informaţii la cea existentă;
·        close – procedură cu ajutorul căreia se închide un fişier text