sub program are

206
Algoritmica si programare/Semestrul I: Programare Œn limbajul Turbo (Borland) Pascal 1. Generalitati 2. Elemente de sintaxa 3. Structura unui program 4. Tipuri de date predefinite 5. Expresii 6. Declaratii si definitii 7. Instructiuni 8. Subprograme Pascal 9. Tipuri de date structurate 10. Structuri dinamice de date 11. Elemente de modularizare 12. Abstractizarea datelor 13. Programarea orientata pe obiecte Capitolul 8. Subprograme Pascal 8.1. Proiectarea top-down 8.2. Abstractizarea subprogramelor 8.3. Rolul subprogramelor Œntr-un program 8.4. Declararea si apelarea procedurilor 8.4.1. Proceduri fara parametri 8.4.2. Proceduri cu parametri de intrare 8.4.3. Proceduri cu parametri de intrare si de iesire 8.5. Declararea si apelarea functiilor 8.6. Apelarea procedurilor si transmiterea parametrilor. Semantica apelului 8.6.1. Parametri transmisi prin valoare 8.6.2. Parametri transmisi prin adresa (referinta) 8.6.3.Verificarea corespondentei parametrilor formali cu cei actuali 8.6.4. Variabile globale si locale Œntr-un subprogram 8.6.5. Reguli de proiectare a subprogramelor 8.7. Subprograme apelate din subprograme 8.7.1. Apelul subprogramelor din subprograme. Subprograme locale Œn subprograme 8.7.2. Apelul recursiv 8.7.3. Subprograme mutual recursive 8.8. Tipuri procedurale 8.8.1. Tipuri Procedure 8.8.2. Tipuri Function 8.1. Proiectarea top-down 1

Upload: mariana-elena-szekely

Post on 23-Jun-2015

677 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Sub Program Are

Algoritmica si programare/Semestrul I: Programare Œn limbajul Turbo (Borland) Pascal

1. Generalitati2. Elemente de sintaxa3. Structura unui program4. Tipuri de date predefinite5. Expresii6. Declaratii si definitii7. Instructiuni8. Subprograme Pascal9. Tipuri de date structurate10. Structuri dinamice de date11. Elemente de modularizare12. Abstractizarea datelor13. Programarea orientata pe obiecte

Capitolul 8. Subprograme Pascal

8.1. Proiectarea top-down8.2. Abstractizarea subprogramelor8.3. Rolul subprogramelor Œntr-un program8.4. Declararea si apelarea procedurilor8.4.1. Proceduri fara parametri8.4.2. Proceduri cu parametri de intrare8.4.3. Proceduri cu parametri de intrare si de iesire8.5. Declararea si apelarea functiilor8.6. Apelarea procedurilor si transmiterea parametrilor. Semantica apelului8.6.1. Parametri transmisi prin valoare8.6.2. Parametri transmisi prin adresa (referinta)8.6.3.Verificarea corespondentei parametrilor formali cu cei actuali8.6.4. Variabile globale si locale Œntr-un subprogram8.6.5. Reguli de proiectare a subprogramelor8.7. Subprograme apelate din subprograme8.7.1. Apelul subprogramelor din subprograme. Subprograme locale Œn subprograme8.7.2. Apelul recursiv8.7.3. Subprograme mutual recursive8.8. Tipuri procedurale8.8.1. Tipuri Procedure8.8.2. Tipuri Function8.1. Proiectarea top-downProiectarea top-down (de sus Œn jos) este o metoda de dezvoltare a programelor prin care problema de rezolvat este descompusa Œn subprobleme mai simple (care se pot rezolva mai usor). La rƒndul sau, fiecare subproblema poate fi din nou descompusa Œn alte subprobleme daca este necesar.Formal, proiectarea top-down sau descompunerea functionala se poate exprima astfel:Se da o problema P care trebuie rezolvata.Problema P se descompune Œn subproblemele P1, P2, ..., Pn astfel Œncƒt:1) Subproblemele Pi (1 = i = n) sunt deduse din specificarea problemei P2) Fiecare subproblema Pi (1 = i = n) se poate rezolva independent3) Fiecare subproblema Pi (1 = i = n) este mai simpla decƒt problema initiala P4) Solutia problemei P se obtine prin compunerea solutiilor subproblemelor P1, P2, ..., Pn

1

Page 2: Sub Program Are

5) Pentru o problema data, descompunerea se opreste cƒnd problema se poate rezolva directLimbajul Pascal pune la dispozitia programatorului urmatoarele instrumente pentru proiectarea top-down:- subprogramele (procedure si function) care corespund subproblemelor- structurile de calcul secventiala, alternativa si repetitiva care se folosesc la compunerea solutiilor subproblemelor- instructiunile de apel de subprograme care se folosesc la determinarea solutiilor subproblemelor si la compunerea acestora (Œn cazul apelului recursiv)O metoda complementara proiectarii top-down este proiectarea bottom-up (de jos Œn sus). Aceasta se bazeaza pe existenta solutiilor pentru unele probleme simple sub forma unor subprograme. Prin compunerea solutiilor acestor subprobleme se obtine solutia problemei initiale fara a fi nevoie derescrierea tuturor subprogramelor necesare.Exemplul 1): Determinarea solutiei ecuatiei de gradul II.Definirea problemei este urmatoarea: Se considera ecuatia de gradul II de forma a*X^2 + b*X + c = 0, unde a, b si c sunt numere reale. Sa se determine radacinile ale acesteia.

Analiza problemei Problema initiala se poate reformula astfel: se dau coeficientii ecuatiei de gradul II a*X^2 + b*X + c = 0 si se cere sa se determine radacinile x1, x2 ale acesteia, reale sau complexe.Descompunerea (nivelul 1) Din acest enunt identificam deja trei subprobleme P1: Citirea datelor de intrare (CitesteDate) P2: Determinarea solutiei ecuatiei de gradul II P3: Afisarea solutiei determinate (AfiseazaRezultate)

Solutia problemei initiale P se obtine prin compunerea solutiilor subproblemelor P1, P2, P3 (aici compunerea Œnseamna rezolvarea lor Œn ordinea enumerarii date).Arborele de structura al programului este:

ÚÄÄÄ¿ ³ P ³ ÀÄÂÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÁÄ¿ ÚÄÁÄÄ¿ ÚÄÁÄÄ¿ ³ P1 ³ ³ P2 ³ ³ P3 ³ ÀÄÄÄÄÙ ÀÄÄÄÄÙ ÀÄÄÄÄÙ

Continuarea descompunerii Subproblema P1: Citirea coeficientilor ecuatiei de gradul II Specificare: Sa se citeasca trei numere reale a, b si c Descompunere (nivelul 2) Se identifica Œn acest caz o singura subproblema, numita P4: CitesteReal care va trebui aplicata (rezolvata) de trei ori: 1) CitesteReal pentru a 2) CitesteReal pentru b 3) CitesteReal pentru cSubproblema P4 (CitesteReal) este suficient de simpla si nu mai trebuieste descompusa (se poate implementa direct)Subproblema P2: Determinarea solutiei ecuatiei de gradul IISpecificare: Sa se rezolve ecuatia de gradul II cu coeficientii a, b si c Descompunere (nivelul 2)

2

Page 3: Sub Program Are

Se identifica Œn acest caz doua subprobleme: P5: verifica daca ecuatia este de gradul II (DateCorecte) P6: determina solutia ec. de gradul II (Prelucreaza) Solutia subproblemei P2 este Daca DateCorecte atunci Prelucreaza Subproblema P3: Afisarea rezultatelor Specificare: Sa se afiseze rezultatele rezolvarii ec. de gradul II Nu mai este nevoie de descompunereArborele de structura al programului este: ÚÄÄÄ¿ ³ P ³ ÀÄÂÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÁÄ¿ ÚÄÁÄÄ¿ ÚÄÁÄÄ¿ ³ P1 ³ ³ P2 ³ ³ P3 ³ ÀÄÄÂÄÙ ÀÄÂÄÄÙ ÀÄÄÄÄÙ ³ ÚÄÄÄÄÄÁÄÄÄÄÄÄ¿ ÚÄÄÁÄ¿ ÚÄÄÁÄ¿ ÚÄÄÁÄ¿ ³ P4 ³ ³ P5 ³ ³ P6 ³ ÀÄÄÄÄÙ ÀÄÄÄÄÙ ÀÄÄÄÄÙ

8.2. Abstractizarea subprogramelorUn subprogram poate fi considerat ca o 'cutie neagra' care primeste date de intrare si furnizeaza rezultate. Proiectarea unui subprogram se realizeaza combinƒnd doua metode:- 1) abstractizarea prin parametrizare- 2) abstractizarea prin specificare1) Abstractizarea prin parametrizare Œnseamna identificarea, pentru o problema data P, a intrarilor (datelor de intrare) si iesirilor (rezultatelor) acesteia.Intrarile si iesirile problemei se vor numi Œn continuare 'parametri'.Parametrii asigura generalitatea problemei si independenta de mediul de apel (programul care va apela problema data va trebui sa furnizeze doar valori concrete pentru parametrii de intrare si sa foloseasca rezultatele furnizate de parametrii de iesire). Independenta de mediul de apel asigura reutilizarea(solutiei) problemei Œn alte locuri decƒt cel pentru care a fost rezolvata initial. Mediul de apel este format din toate variabilele care au in domeniul lor de vizibilitate punctul apelului (atƒt cele declarate Œn programul apelant, deci locale Œn acesta, cƒt si cele declarate Œn blocuri exterioare programului apelant).Din punctul de vedere al comunicarii cu mediul extern (care Œl poate apela),un subprogram P are trei tipuri de parametri- parametri de intrare: introduc informatie Œn P fara a fi modificati Œn timpul executiei acestuia (IN)- parametri de iesire: scot informatie (rezultate) din P; nu intereseaza valorile cu care acesti parametri intra Œn P (OUT)- parametri de intrare-iesire: sunt concomitent parametri de intrare si de iesire pentru P (IN-OUT)2) Abstractizarea prin specificare ataseaza fiecarei probleme o specificare.Specificarea unei probleme asigura independenta folosirii acesteia de modul de rezolvare a ei (altfel spus de algoritmul folosit pentru rezolvare). Unei aceleiasi specificatii Œi pot corespunde mai multe implementari; toate aceste implementari vor avea Œn comun aceeasi maniera de apel a procedurii. Prinspecificare se precizeaza ce trebuie sa faca problema si nu cum face.Specificarea contine precizarea numelui problemei, a parametrilor acesteia si a unor asertiuni (predicate) numite pre-conditii si post-conditii:- pre-conditiile precizeaza cerintele impuse asupra datelor de intrare pentru ca subprogramul sa se execute corect.- post-conditiile precizeaza efectele executiei subprogramului asupra datelor

3

Page 4: Sub Program Are

de intrare/iesire (care formeaza Œmpreuna starea programului).De obicei specificarea unei probleme se face dupa ce s-au stabilit parametrii acesteia sau simultan cu parametrizarea. Dupa ce specificarea s-a Œncheiat, se poate trece la implementarea acesteia. Unei specificari pot sa-i corespunda mai multe implementari diferite (folosind, de exemplu, algoritmi diferitipentru transformarea datelor de intrare Œn date de iesire).Specificarea serveste Œn primul rƒnd celui care foloseste rezolvarea problemei. Din acest punct de vedere, ea trebuie privita ca un contract Œntre cel care a rezolvat problema si cel care vrea sa foloseasca rezolvarea respectiva Œntr-un program. Cƒt timp specificarea nu se schimba, toti clientii acesteia (toti programatorii care apeleaza o implementare sau alta a ei) vor obtine aceleasi rezultate pe aceleasi date de intrare, folosind acelasi apel (indiferent de implementarea folosita).Parametrizarea si specificarea sunt legate Œntre ele, ambele concurƒnd la realizarea a ceea ce am numit 'abstractizarea procedurala'. In paragraful urmator vom discuta acest lucru din punctul de vedere al folosirii procedurilor (subprogramelor) Œntr-un program.

Pentru exemplul nostru:ParametrizareaP1 - CitesteDate are trei parametri de iesire, care vor fi notati cu a, b si cP4 - CitesteReal are un singur parametru de iesire- numarul real citit xP5 - DateCorecte are doi parametri: a - coeficientul lui x2 (parametru de intrare) corect - parametru de iesire corect = 0 - ecuatia este de gradul II (a ? 0) corect = 1 - ecuatia nu este de gradul II (a = 0)P6 - Prelucreaza are urmatorii parametri a, b, c - coeficientii ecuatiei de gradul II (parametri de intrare) x1, x2 - solutia ecuatiei (parametri de iesire) cod - informatie privind solutia (parametru de iesire) cod = 2 - radacini reale diferite cod = 3 - radacini reale egale cod = 4 - radacini complexeP3 - AfiseazaRezultate are urmatorii parametri x1, x2 - solutia ecuatiei (parametri de intrare) cod - informatie privind solutia (parametru de intrare) cod = 1 - ecuatia nu este de gradul II cod = 2 - radacini reale diferite cod = 3 - radacini reale egale cod = 4 - radacini complexeSpecificarea P1 - CitesteDate(a, b, c) Pre: True Post: a, b, c î R P4 - CitesteReal(x) Pre: True Post: x î R P5 - DateCorecte(a, corect) Pre: a î R Post: corect î {0, 1} corect = 1 daca ecuatia este de gradul II corect = 0 Œn caz contrar

P6 - Prelucreaza(a, b, c, x1, x2, cod) Pre: a, b, c î R si a <> 0

4

Page 5: Sub Program Are

Post: x1, x2 solutii ale ecuatiei a*X^2 + b*X + c = 0 cod î {2, 3, 4} cod = 2 - radacini reale diferite cod = 3 - radacini reale egale cod = 4 - radacini complexe

P3 - AfiseazaRezultate(x1, x2, cod) Pre: x1, x2 î R cod î {1, 2, 3, 4} Post: True

8.3. Rolul subprogramelor Œntr-un programPrima ratiune de folosire a subprogramelor Œntr-un program este mai buna structurare a acestuia. Folosind subprograme, corpul programului (numit si program principal) se exprima printr-un numar mai mic de instructiuni si este mai usor de Œnteles. De asemenea, programul este mai usor de modificat.Exemplu: vezi structurile de programe discutate anterior.A doua ratiune de folosire a subprogramelor Œntr-un program este evitarea repetarii unor grupuri de instructiuni identice sau aproape identice.Un exemplu foarte simplu este chiar citirea coeficientilor ecuatiei de gradul II, care Œnseamna repetarea de trei ori a aceluiasi grup de instructiuni (afiseaza mesaj, citeste coeficient). Prin parametrizare, grupurile de instructiuni aproape identice se pot transforma Œn subprograme. Folosind apoi valori corespunzatoare pentru parametri, fiecare apel realizeaza functia sa dorita (grupul de instructiuni specific).A treia ratiune de folosire a subprogramelor Œntr-un program este refolosirea lor (apelarea lor Œn programe diferite). Daca subprogramele sunt suficient de generale si daca ele necesita un volum mare de munca (de programare si de testare), atunci este rational ca, odata ce au fost realizate, sa se poata apela oriunde este nevoie de ele, fara a le rescrie. Pentru aceasta, Turbo Pascal pune la dispozitia programatorului (Œncepƒnd din versiunea 4.0) modulele (numite unit-uri). Un astfel de modul contine (simplist vorbind) subprograme care se pot folosi Œn alte programe. Pentru a le putea folosi, este suficient sa includem numele unit-ului Œntr-o fraza Uses pusa la Œnceputul zonei de declaratii a programului Pascal. Mediul Turbo Pascal are o serie Œntreaga de unit-uri care contin subprograme ce se pot apela Œn programele pe care le realizam. Versiunile anterioare (sub 4.0) ofera o directiva de compilare ce permite includerea unui fisier sursa (care contine numai subprogramul refolosit) Œntr-un alt fisier sursa ($I).Prin urmare, Œntr-un program Pascal putem folosi urmatoarele categorii de subprograme:- subprograme standard: se gasesc Œn unit-ul System (care este folosit implicit de catre orice program Turbo Pascal); exemple: Read, ReadLn, Write, WriteLn, functiile matematice, subprogramele de lucru cu stringuri, etc.- subprograme locale programului: sunt declarate Œn zona de declaratii a acestuia si se pot folosi (apela) numai Œn programul respectiv- subprograme de biblioteca: sunt declarate Œn module (ale mediului sau realizate de programator); se pot apela daca programul contine Œn clause Uses numele unit-ului (modulului) Œn care sunt declarate.In toate cazurile, subprogramele se pot apela (folosi) numai dupa ce au fost declarate. Apelul de subprogram este considerat Œn Pascal ca instructiune simpla si Œnseamna urmatoareleProgram apelant P Subprogram SP[declaratii] [declaratii]

begin ( corpul P } ÚÄ> begin { corpul SP } ... ³ instr_1; apel SP ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ...

5

Page 6: Sub Program Are

instr_dupa_apel <ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ instr_n; ... ÀÄÄ end; { SP }end. { P }

La Œntƒlnirea instructiunii de apel, controlul executiei este predate subprogramului apelat (subprogramul SP). Executia instructiunilor din programul apelant se opreste si Œncepe executia instructiunilor din corpul subprogramului SP. La terminarea executiei corpului SP (atingerea instructiunii end; sau Œntƒlnirea unei instructiuni Exit - vezi cap 7), se reia executia instructiunilor din programul apelant cu instr_dupa_apel.Ce variabile se pot folosi Œn corpul SP?- variabile locale (cele declarate Œn SP)- parametrii SP (daca are)- variabilele globale din programul apelant (numai daca SP este subprogram local al lui P)Din punctul de vedere al comunicarii cu programul apelant, subprogramele se pot Œmparti Œn:- subprograme care nu folosesc mediul programului apelant (deci nu au nevoie de parametri si nu recurg la folosirea unor variabile globale)- subprograme ce comunica cu mediul programului apelant prin variabile globale si/sau parametri- subprograme ce comunica cu mediul programului apelant numai prin parametriDintre categoriile expuse mai sus, prima si a treia contin subprograme ce se pot include Œn module si deci refolosi Œn mai multe programe. In a doua categorie (care foloseste variabile locale din programul apelant care sunt globale Œn el) se includ subprogramele locale ale acestuia, iar declaratia lor trebuie sa apara dupa declaratia variabilelor folosite (atƒt declaratia variabilelor globale, cƒt si declaratia de subprogram vor face parte din zona de declaratii a programului apelant, Œnsa acele variabile care se folosesc Œn corpul subprogramului trebuie declarate Œnainte de declararea subprogramului).Exemplu:Program P1a; { foloseste un subprogram local, P2 } Var Contor: Integer; Procedure P2; Begin WriteLn('Valoarea lui Contor este', Contor:4) End; {P2} Begin {programul principal} For Contor := 1 To 3 Do P2 End; { P1a }

Programul P1a apeleaza subprogramul local P2, care nu are parametri si carefoloseste variabila globala Contor, declarata Œnainte de declaratia lui P2.El va afisa ca rezultatValoarea lui Contor este 1Valoarea lui Contor este 2Valoarea lui Contor este 3Programul functioneaza corect deoarece P2 este Œn domeniul de vizibilitate al variabilei Contor.Daca Œnsa declaratia variabilei Contor apare dupa declaratia subprogramului P2, ca Œn exemplul urmator,Program P1b; { foloseste un subprogram local, P2 } Procedure P2; Begin WriteLn('Valoarea lui Contor este', Contor:4) { aici se va produce eroare de compilare: Contor este

6

Page 7: Sub Program Are

identificator necunoscut } End; {P2} Var Contor: Integer; Begin {programul principal} For Contor := 1 To 3 Do P2 End; { P1b }atunci se va produce o eroare de compilare la compilarea lui P1b, deoarece identificatorul Contor (referit acolo) nu este Œnca declarat (P2 nu este Œn DoV al variabilei Contor si refera aceasta variabila).Cum se corecteaza eroarea de mai sus?- varianta 1 (nerecomandata): Programul P1a- varianta 2 (recomandata): Programul P1c - Contor devine parametru al lui P2Program P1c; { foloseste un subprogram local, P2 } Procedure P2(C: Integer); Begin WriteLn('Valoarea lui Contor este', C:4) End; {P2} Var Contor: Integer; Begin {programul principal} For Contor := 1 To 3 Do P2(Contor) End; { P1c }Sfaturi:- Concepeti orice subprogram astfel Œncƒt el sa poata fi inclus Œntr-un modul.- Nu folositi Œntr-un subprogram variabile globale.- In locul variabilelor globale folositi parametri.- Pentru a fi siguri ca subprogramele locale nu folosesc variabile globale, puneti declaratiile de variabile (Var) dupa declaratiile de subprograme (ca Œn programul P1c). Aceasta va garanta faptul ca variabilele globale se vor folosi numai Œn corpul programului, nu si Œn subprograme.

In Pascal exista doua feluri de subprograme: procedura si functie.Subprogramele de tip procedura realizeaza o operatie bine precizata si apelul lor este considerat instructiune simpla. Subprogramele de tip functie calculeaza o valoare si apelul lor este considerat operand Œntr-o expresie.8.4. Declararea si apelarea procedurilor8.4.1. Proceduri fara parametriProcedurile fara parametri executa anumite operatii independente de mediul programului. Un astfel de exemplu este procedura standard ClrScr aflata Œn unit-ul Crt, care realizeaza stergerea ecranului. Alte exemple de proceduri standard fara parametri sunt Break, Continue si Exit din unit-ul System (discutate Œn cap. 7).Sintaxa de declarare a unei proceduri fara parametri este:Procedure Nume_Proc; [declaratii] Begin { corpul procedurii } instr_1; ... instr_n End; { Nume_Proc }unde- Procedure, Begin, End sunt cuvinte rezervate- Nume_Proc este numele procedurii (identificator)

7

Page 8: Sub Program Are

- declaratia de procedura introduce un bloc- prima linie a procedurii se numeste antet (la fel ca la programul principal)- zona declaratii contine declaratiile locale Œn procedura, care vor avea ca DoV blocul procedurii; se pot include aici toate tipurile de declarati discutate [DoV=domeniu de vizibilitate;DuV=durata de viata]- instr_1, ..., instr_n sunt instructiuni Pascal (simple sau compuse)Apelul unei asemenea proceduri se face printr-o instructiune care contine doar numele procedurii.Exemplu:[declarare]Procedure Afiseaza_Titlu; Begin ClrScr; WriteLn('Ce face programul') End; { Afiseaza_Titlu }[apelare]begin ... Afiseaza_Titlu; ....End.Conceperea unei proceduri fara parametri Œnseamna de fapt (pentru limbajul Pascal, si nu numai) realizarea unei noi instructiuni simple a limbajului.Odata procedura declarata, aceasta Œnseamna pentru Pascal o noua instructiune simpla, care grupeaza (se traduce Œn) toate instructiunile continute Œn corpul ei. Aceasta este cea mai simpla modalitate de extindere a limbajului cu noi instructiuni, din ce Œn ce mai complexe. Unit-urile mediului Turbo (Borland) Pascal contin o serie de astfel de proceduri.8.4.2. Proceduri cu parametri de intrareAceste proceduri nu modifica mediul programului care le apeleaza. Rolul lor este de a efectua prelucrari, de a verifica anumite conditii sau de a afisa anumite rezultate.Sintaxa de declarare a unei proceduri cu parametri de intrare este:Procedure Nume_Proc(<lista_parametri_formali>); [declaratii] Begin { corpul procedurii } instr_1; ... instr_n End; { Nume_Proc }unde toate precizarile de la procedurile fara parametri ramƒn valabile si- lista_parametri_formali este o lista de declaratii de parametri formali de forma: <lista_parametri_formali> ::= {<declaratie parametri formali>} <declaratie parametri formali> ::= <lista parametri>:<tip_de_date>;

- ultima <declaratie parametri formali> nu se Œncheie cu ;Lista de parametri formali se poate transcrie Œn forma: <lista_parametri_formali> ::= pf1: t1; pf2: t2; ..., pf_n: t_nunde pf1, pf2, ..., pf_n sunt parametrii formali, iar t1, t2, ..., t_n sunt tipurile de date asociate acestora.Apelul unei asemenea proceduri se face printr-o instructiune care contine numele procedurii, urmata de lista parametrilor actuali: Nume_Proc(<lista_parametri_actuali>);unde <lista_parametri_actuali> este o lista de expresii, care trebuie sa corespunda cu <lista_parametri_formali>

8

Page 9: Sub Program Are

<lista_parametri_actuali> ::= pa1, pa2, ..., pa_nCorespondenta dintre <lista_parametri_actuali> si <lista_parametri_formali>Œnseamna urmatoarele (vezi si 8.6.3):- cele doua liste trebuie sa aiba acelasi numar de elemente- elementul de pe pozitia i din <lista_parametri_actuali> trebuie sa fie de tipul de date ti (cum apare el precizat Œn <lista_parametri_formali>)- altfel spus - pa1 trebuie sa fie de tipul t1 - pa2 trebuie sa fie de tipul t2 - ... - pan trebuie sa fie de tipul t_n

Exemplu:Program P2a; { foloseste o procedura locala, ScrieSuma } Procedure ScrieSuma(A, B, C: Integer); Var S: Integer; Begin S := A + B + C; WriteLn('Suma numerelor este', S:4); A := 0; B := 0; C := 0 End; {ScrieSuma} Var N1, N2, N3: Integer; Begin {programul principal} N1 := 12; N2 := 23; N3 := 34; WriteLn('Se aduna numerele: ', N1:3, N2:3, N3:3); ScrieSuma(N1, N2, N3); WriteLn('S-au adunat numerele: ', N1:3, N2:3, N3:3) End; { P2a }

In exemplul de mai sus, ScrieSuma contine o parte de declaratii locale, Œn care este declarata variabila Œntreaga S. Aceasta variabila are ca domeniu de vizibilitate corpul procedurii ScrieSuma si poate fi referita numai Œn acesta.Daca se Œncearca referirea ei Œn programul principal, se va produce o eroare de compilare.Parametrii formali ai procedurii ScrieSuma sunt declarati astfel: A, B, C: Integer,declaratie echivalenta cu A: Integer; B: Integer, C: Integer (cƒnd mai multi parametri formali consecutivi au acelasi tip de date, ei pot fi pusi Œntr-o lista (numele lor se separa prin virgule) si se scrie o singura data tipul (la fel ca la declaratia de variabile). Declararea parametrilor formali ai procedurii se poate considera tot o declaratie de variabile locale: acestia au semnificatie doar atƒta timp cƒt procedura se executa.Antetul procedurii ScrieSuma stabileste modalitatea Œn care aceasta poate fi apelata. Prin urmare am dedus o regula importanta: declaratia de procedura stabileste modul de apel al acesteia. Orice apel care nu se conformeaza declaratiei va fi respins (Œn cazul limbajului Pascal de catre compilator).

9

Page 10: Sub Program Are

Apelul procedurii ScrieSuma din exemplul de mai sus se conformeaza regulii enuntate. Toti parametrii actuali de intrare pot fi orice expresii care se pot evalua Œn momentul apelului (Œn cazul nostru ei sunt expresii simple, formate din variabile declarate Œn zona de declaratii a programului apelant).Executia acestui program va produce afisarea urmatoarelor mesaje:Se aduna numerele: 12 23 34Suma numerelor este 69S-au adunat numerele: 12 23 348.4.3. Proceduri cu parametri de intrare si de iesireDe cele mai multe ori, o procedura trebuie sa efectueze o operatie care modifica mediul programului apelant. Am vazut anterior ca acest lucru se poate face Œn doua moduri:- prin modificarea unei variabile globale (declarata Œn programul appellant Œnainte de declaratia procedurii apelate, care este vizibila Œn corpul procedurii)- prin transmiterea modificarii spre programul apelant printr-un parametru de iesireApare aici un element nou: cum se poate extrage (scoate) din procedura o valoare calculata Œn ea si apoi aceasta sa se faca disponibila Œn programul apelant, care sa o poata folosi Œn continuare? Cu alte cuvinte, cum se specifica faptul ca un parametru formal de procedura este parametru de iesire?

Limbajul Pascal pune la dispozitia programatorului un mecanism simplu de specificare a parametrilor de iesire, prefixƒnd declaratia acestora cu cuvƒntul rezervat 'Var'. Pƒna acum am vazut ca 'Var' este folostit pentru a preciza declaratii de variabile. Acum apare a doua folosire a sa, specificarea parametrilor de iesire.

Sintaxa declaratiei de procedura cu parametri de intrare si de iesire este identica cu cea pentru procedurile cu parametri de intrare, cu singura deosebire ca declaratiile parametrilor formali de iesire trebuie prefixate cu Var. Din nou, daca o procedura are mai multi parametri de iesire de acelasi tip, si care sunt consecutivi, declararea acestora se poate condensa.

Apelarea unei proceduri cu parametri de intrare si de iesire se face la fel ca Œn cazul procedurilor cu parametri de intrare. Exista Œnca o restrictie privitoare la parametrii actuali de iesire. Daca parametrii actuali de intrare pot sa fie orice expresii care se pot evalua Œn momentul apelului, parametrii actuali de iesire trebuie sa fie obligatoriu variabile care au punctual apelului Œn domeniul lor de vizibilitate.

Exemple:Declaratia de procedura

Procedure Imparte(a, b: Integer; Var c, r: Integer); {Œmpartirea numerelor a si b produce cƒtul c si restul r}

este echivalenta cuProcedure Imparte(a: Integer; b: Integer; Var c: Integer; Var r: Integer);In declaratiile de mai sus, a si b sunt parametri de intrare, iar c si r sunt parametri de iesire.

Exemplul prezentat Œn paragraful precedent se poate rescrie aici astfel:Program P2b; { foloseste o procedura locala, CalculSuma } Procedure CalculSuma(A, B, C: Integer; Var S: Integer); Begin S := A + B + C; A := 0; B := 0; C := 0 End; {CalculSuma}

10

Page 11: Sub Program Are

Var N1, N2, N3, Suma: Integer; Begin {programul principal} N1 := 12; N2 := 23; N3 := 34; WriteLn('Se aduna numerele: ', N1:3, N2:3, N3:3); CalculSuma(N1, N2, N3, Suma); WriteLn('Suma numerelor este', Suma:4); WriteLn('S-au adunat numerele: ', N1:3, N2:3, N3:3) End; { P2b }

In exemplul de mai sus, CalculSuma nu mai contine declaratia locala a variabilei Œntregi S, aceasta transformƒndu-se Œn parametru de iesire, fiind declarat ca atare Œn antetul procedurii.Parametrii formali ai procedurii CalculSuma sunt declarati astfel A, B, C: Integer; var S: Integer.Aceasta declaratie specifica faptul ca primii trei sunt parametri de intrare, iar S este parametru de iesire.Daca analizam programul principal, constatam ca:- s-a declarat o noua variabila Œntreaga, Suma- apelul procedurii CalculSuma contine patru parametri actuali, primii trei cu semnificatia din exemplul anterior, iar al patrulea pe post de parametru de iesire (Œn cazul nostru variabila Suma joaca acest rol, valoarea ei fiind folosita Œn continuare pentru afisare- afisarea sumei numerelor s-a transferat din subprogram Œn programul appellant (motiv pentru care numele subprogramului s-a schimbat)Executia acestui program va produce afisarea acelorasi mesaje ca si cele de la programul precedent:Se aduna numerele: 12 23 34Suma numerelor este 69S-au adunat numerele: 12 23 34

8.5. Declararea si apelarea functiilor

O categorie speciala de subprograme este formata din functii. Scopul unei functii este calculul unei valori, care este Œntoarsa (folosita) Œn programul apelant. Asemanarile dintre proceduri si functii sunt urmatoarele- ambele sunt subprograme- declaratiile lor formeaza un bloc- ambele au parametri.Deosebirile dintre proceduri si functii sunt cel putin urmatoarele:- functiile Œntorc un singur rezultat, de un tip simplu (Œn Pascal)- apelul de procedura este considerat Œn Pascal instructiune, pe cƒnd apelul de functie este considerat operand Œntr-o expresie- functiile nu trebuie sa aiba parametri de iesire- la functii trebuie precizat tipul rezultatului intors (calculat), care este folosit in programul apelantDin punct de vedere sintactic, declaratia de functie are forma:Function Nume_Functie[(<lista_parametri_formali>)]: tip_rezultat; [declaratii] Begin { corpul functiei } instr_1; ... instr_n End; { Nume_Functie }

11

Page 12: Sub Program Are

unde- Function, Begin, End sunt cuvinte rezervate- Nume_Functie este numele functiei (identificator)- lista_parametri_formali este o lista de declaratii de parametri formali (poate sa lipseasca)- tip_rezultat este tipul de data al rezultatului Œntors de functie (este obligatoriu)- declaratia de functie introduce un bloc - prima linie a declaratiei de functie se numeste antet (la fel ca la programul principal) - zona declaratii contine declaratiile locale Œn functie, care vor avea ca DoV blocul functiei; se pot include aici toate tipurile de declarati discutate- instr_1, ..., instr_n sunt instructiuni Pascal (simple sau compuse); Œntre aceste instructiuni trebuie sa apara cel putin odata o instructiune de atribuire de forma Nume_Functie := expresie;Apelul unui subprogram de tip functie se face identic cu cel de procedura, adica se scrie numele subprogramului (urmat Œn paranteza de parametrii actuali, daca functia are parametri). Ceea ce difera este semnificatia apelului: deoarece functia Œntoarce o valoare, apelul de functie are semantica unui operator Œntr-o expresie si nu semantica unei instructiuni simple, ca apelul de procedura. Prin urmare, apelul de functie poate sa apara oriunde poate apare o expresie de tipul sau.Exemplul urmator este echivalent cu cele prezentate anterior, folosind de aceasta data un subprogram de tip functie:

Program P2c; { foloseste o functie locala, CalculSuma } Function CalculSuma(A, B, C: Integer): Integer; Begin CalculSuma := A + B + C; A := 0; B := 0; C := 0 End; {CalculSuma} Var N1, N2, N3: Integer; Begin {programul principal} N1 := 12; N2 := 23; N3 := 34; WriteLn('Se aduna numerele: ', N1:3, N2:3, N3:3); WriteLn('Suma numerelor este', CalculSuma(N1, N2, N3):4); WriteLn('S-au adunat numerele: ', N1:3, N2:3, N3:3) End; { P2c }

In exemplul de mai sus, CalculSuma nu mai contine declaratia locala a variabilei Œntregi S, si nici declaratia parametrului de iesire S; rezultatul Œntors de functie va avea semnificatia lui S din exempleleprecedente. Acest lucru se specifica folosind Œn corpul functiei numele acesteia Œn membrul stƒng al operatiei de atribuire: CalculSuma := A + B + C;.Parametrii formali ai functiei CalculSuma sunt declarati la fel ca Œnexemplul P2a.Daca analizam programul principal, constatam ca:- nu mai este nevoie de variabila Œntreaga Suma- apelul functiei CalculSuma apare ca parametru al instructiunii de afisare a rezultatului:

WriteLn('Suma numerelor este', CalculSuma(N1, N2, N3):4);

12

Page 13: Sub Program Are

Executia acestui program va produce afisarea acelorasi mesaje ca si cele dela programele precedente:Se aduna numerele: 12 23 34Suma numerelor este 69S-au adunat numerele: 12 23 34

8.6. Apelarea procedurilor si transmiterea parametrilor. Semantica apelului

Apelul unui subprogram Œnseamna- determinarea (localizarea) subprogramului apelat (din numele acesteia); reaminitim ca subprogramul apelat poate fi: - subprogram local (ca Œn exemplele P2a, P2b, P2c de mai sus) - subprogram standard (aflat Œn unit-ul System) - subprogram declarat Œntr-un alt unit (al carui nume apare Œntr-o clauza Uses si care a fost Œn prealabil creat - fie ca este un unit al mediului, fie ca este un unit realizat de un programator)- verificarea corectitudinii apelului (dupa ce s-a localizat subprogramul, antetul lui este disponibil si deci si lista de parametri formali)- evaluarea parametrilor actuali si stabilirea corespondentei dintre acestia si parametrii formali- executia subprogramului, Œn care parametrii formali primesc la Œnceput valorile parametrilor actuali- la terminarea executiei se preda controlul programului apelant, care continua executia cu instructiunea de dupa apelIn ce cazuri apelul unui subprogram nu este corect (si compilatorul da un mesaj de eroare)?- subprogramul apelat nu este declarat (sau s-a scris gresit numele Œn apel)- lista parametrilor actuali nu corespunde cu lista de parametri formali- DoV al subprogramului apelat nu contine punctul (locul) de unde s-a facut apelulVom discuta la Œnceput modalitatile de transmitere a parametrilor de intrare si de iesire si apoi vom analiza modul cum se face verificarea corespondentei parametrilor actuali cu cei formali.

8.6.1. Parametri transmisi prin valoare

Vom comenta Œn cele ce urmeaza programul P2a. Presupunem ca s-a facut verificarea corespondentei dintre parametrii actuali din apelul procedurii ScrieSuma si ca acestia corespund cu parametrii formali precizati Œn declaratia procedurii.Constatam ca numarul parametrilor actuali si tipul acestora coincide (parametru cu parametru), deci din punct de vedere formal conditiile de apelare sunt Œndeplinite. Ce se Œntƒmpla mai departe pentru executia apelului?Urmatorul pas este evaluarea parametrilor actuali si punerea lor Œn corespondenta cu cei formali. Evaluarea este simpla: parametrii actuali au fost initializati anterior, deci au valori bine determinate, iar corespondenta se stabileste astfel:- N1 este pus Œn corespondenta cu A- N2 este pus Œn corespondenta cu B- N3 este pus Œn corespondenta cu CPunerea Œn corespondenta Œnseamna (Œn cazul nostru) copierea valorilor parametrilor actuali Œn parametrii formali corespunzatori, deci se poate scrie printr-o secventa de atribuiri: A := N1; { atribuie lui A valoarea expresiei N1 } B := N2; { atribuie lui B valoarea expresiei N2 } C := N3; { atribuie lui C valoarea expresiei N3 }Explicatia de mai sus justifica de ce se pot folosi expresii pe post de parametri formali: acestea trebuie doar sa se poata evalua Œn momentulapelului.Acum procedura ScrieSuma se poate executa. Prima instructiune din corpul ei realizeaza calcului sumei S := A + B + C;care va folosi valorile parametrilor initializate anterior. Valoarea lui S este afisata apoi pe ecran Œn a doua instructiune, iar urmatoarele trei instructiuni initializeaza pe A, B si C cu 0. Dupa aceasta se

13

Page 14: Sub Program Are

Œntƒlneste cuvƒntul rezervat 'end', adica s-a terminat corpul procedurii. Prin urmare, s-a terminat si executia procedurii ScrieSuma si se revine Œn programul apelant, la instructiunea urmatoare apelului. Aceasta va afisa valorile dinainte de apel ale variabilelor N1, N2 si N3, cu toate ca parametrii corespunzatori lor au fost modificati Œn timpul executiei corpului procedurii ScrieSuma.Motivul pentru care se Œntƒmpla asa este acela ca modificarea valorilor parametrilor formali Œn ScrieSuma nu influenteaza mediul de apel (adica valorile parametrilor actuali din programul apelant) deoarece parametrii formali sunt tratati drept variabile locale ale subprogramului si ca la terminarea executiei acestuia ei se distrug - nu mai are loc o atribuire inversa de valori de forma: N1 := A:; N2 := B; N3 := C;Cu alte cuvinte, A, B si C sunt doar copii ale parametrilor actuali N1, N2 si N3 si modificarea valorii lor nu influenteaza valorile originalelor.Aceasta modalitate de transmitere a parametrilor catre un subprogram (procedura sau functie) se numeste 'transmitere prin valoare'. Fiecarui parametru actual transmis prin valoare Œi corespunde Œn subprogramul apelat o pseudo-variabila locala, care are chiar numele parametrului formal corespunzator.Concluzia acestei analize este urmatoarea: Parametrii actuali transmisi prin valoare dintr-un program apelant P spre un subprogram apelat SP Œsi pastreaza valorile dinainte de apelul SP, indiferent daca parametrii formali corespunzatori lor se modifica sau nu Œn SP.

8.6.2. Parametri transmisi prin adresa (referinta)Programul P2b contine o procedura ce are si parametri de iesire. Presupunem ca s-a facut verificarea corespondentei dintre parametrii actuali din apelul procedurii CalculSuma si ca acestia corespund cu parametrii formali precizati Œn declaratia procedurii.Urmatorul pas este evaluarea parametrilor actuali si punerea lor Œn corespondenta cu cei formali. Primii trei parametri actuali au fost initializati anterior (si sunt transmisi prin valoare), deci au valori bine determinate, iar corespondenta se stabileste astfel:- N1 este pus Œn corespondenta cu A- N2 este pus Œn corespondenta cu B- N3 este pus Œn corespondenta cu Cconform atribuirilor: A := N1; { atribuie lui A valoarea expresiei N1 } B := N2; { atribuie lui B valoarea expresiei N2 } C := N3; { atribuie lui C valoarea expresiei N3 }Al patrulea parametru actual, Suma, este variabila globala Œn P2b care Œnca n-a fost initializata. De fapt, pƒna la apelul CalculSuma(N1, N2, N3, Suma);n-am avut nevoie de ea. Mai mult, vrem sa-i determinam valoarea prin acest apel si s-o folosim mai departe (afisƒnd-o).Parametru actual Suma din apel Œi corespunde parametrul formal S din antetul procedurii, care este declarata asfel: Procedure CalculSuma(A, B, C: Integer; Var S: Integer);Acum apare deosebirea fata de transmiterea prin valoare. Am spus anterior ca S este Œn procedura un parametru formal de iesire si ca am marcat acest lucru prefixƒnd Œn antet declaratia lui cu 'Var'. Ce Œnseamna de fapt acest lucru?Un parametru formal declarat cu Var se transmite prin referinta (adresa), adica corespondenta dintre parametrul actual si cel formal Œn acest caz Œnseamna egalitate de adresa (si nu de valoare, ca Œn cazul transmiterii prin valoare).Prin urmare, corespondenta dintre parametrul actual Suma din apelul procedurii CalculSuma si parametrul formal S din declaratia acesteia se stabileste prin atribuirea adresei variabilei Suma (parametrul actual) ca adresa a parametrului formal S:

14

Page 15: Sub Program Are

adresa(S) := adresa(Suma);Explicatia de mai sus justifica de ce nu se pot folosi expresii pe post de parametri formali la parametrii de iesire: o expresie nu are asociata o adresa fixa Œn memorie (pe timpul evaluarii sale poate fi de exemplu memorata Œntr-un registru al UC). Mai mult, nu se justifica ca un parametru de iesire sa fie o expresie.Acum procedura CalculSuma se poate executa. Prima instructiune din corpul ei realizeaza calcului sumei S := A + B + C;care va folosi valorile parametrilor initializate anterior si va modifica valoarea variabilei locale S. Dar cum S si Suma au aceeasi adresa (si sunt de acelasi tip), se va modifica concomitent si valoarea variabilei Suma (asa cum este ea vazuta Œn programul apelant). Urmatoarele trei instructiuni initializeaza pe A, B si C cu 0 si executia corpului procedurii se termina, revenindu-se Œn programul principal.Urmatoarea instructiune de dupa apel realizeaza chiar afisarea valorii variabilei Suma (care am vazut cum s-a modificat Œn programul apelat).Modalitate de transmitere a parametrilor catre un subprogram (procedura sau functie) explicata Œn aceasta sectiune se numeste 'transmitere prin adresa' sau 'transmitere prin referinta'. Pentru fiecare parametru actual 'pa' transmis prin referinta, parametrul formal corespunzator 'pf' din subprogramul apelatva avea aceeasi adresa cu el: adresa(pf) := adresa(pa);pe toata durata de executie a subprogramului si orice modificare a valorii parametrului formal 'pf' Œn subprogram va avea ca efect imediat si modificarea valorii parametrului actual 'pa'.Procedura CalculSuma are atƒt parametri transmisi prin valoare, cƒt si parametri transmisi prin referinta. Modul Œn care acestia se comporta este ilustrat de rezultatele executiei programului P2b.Concluzia acestei analize este urmatoarea: Parametrii actuali transmisi prin referinta dintr-un program apelant P spre un subprogram apelat SP Œsi modifica valorile simultan cu parametrii formali corespunzatori lor din SP.

8.6.3.Verificarea corespondentei parametrilor formali cu cei actualiAm Œnceput sa discutam aceasta problema Œn 8.4.2, dar acolo Œnca nu aveam toate elementele necesare pentru o prezentare completa a problemei. Acum putem sa afirmam ca verificarea se refera la cele doua liste (lista parametrilor formali, precizata Œn declararea subprogramului si lista parametrilor actuali, precizata Œn apel) si ca se verifica:- numarul parametrilor transmisi (acelasi Œn ambele liste)- tipul acestora (parametrii de pe pozitia 'i 'din ambele liste trebuie sa fie de acelasi tip)- modalitatea de transmitere (parametrii de pe pozitia 'i' din lista de parametri actuali trebuie sa poata fi transmis prin modalitatea de transmitere specificata Œn lista de parametri formali pentru al 'i'-lea parametru formal).Ne vom referi Œn continuare la programul P2b prezentat anterior. Apelul procedurii ScrieSuma stabileste o corespondenta Œntre parametrii formali si parametrii actuali. Apelul este realizat prin instructiuneaScrieSuma(N1, N2, N3);si primul pas care-l face compilatorul este sa stabileasca daca apelul este corect.Cum stabileste compilatorul ca apelul s-a facut corect? Ne aducem aminte ca Pascal este un limbaj puternic tipizat, ceea ce Œnseamna Œntre altele ca tipul oricarei expresii poate fi determinat la compilare. In cazul nostru, expresiile care corespund parametrilor actuali sunt chiar identificatori de variabile, deci expresii simple. Tipul acestor expresii este dedus Œn acest caz chiar din declaratia acestora N1, N2, N3: Integer;ce apare Œn partea de declaratii a programului principal.

15

Page 16: Sub Program Are

8.6.4. Variabile globale si locale Œntr-un subprogramAm discutat Œn capitolul 6 (Declaratii) despre domeniul de vizibilitate al unui identificator Œn general si despre domeniul de vizibilitate si durata de viata a variabilelor. Exemplul urmator, pe care-l vom comenta, contine atƒt variabile globale cƒt si variabile locale si exista o pereche de declaratii pentru aceeasi variabila.Program P3;{ Domeniul de vizibilitate al variabilelor - variabile locale si globale } Var Contor : Integer; { globala } Indice : Integer; { globala } Procedure Afiseaza_Date; Var Contor, { locala } Alta : Integer; { locala } Begin Contor := 7; Writeln('In Afiseaza_Date Contor =', Contor:5, ' Indice =',Indice:5); End; { Afiseaza_Date } Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit } Begin { Programul principal } For Indice := 1 to 3 do Begin Contor := Indice; Writeln('In programul principal Contor =',Contor:5, ' Indice =',Indice:5); Afiseaza_Date; Writeln('In programul principal Contor =',Contor:5, ' Indice =',Indice:5); Writeln; End; { For } Scrie_Sfarsit End. { Programul principal }

{ Rezultatul executieiIn programul principal Contor = 1 Indice = 1In Afiseaza_Date Contor = 7 Index = 1In programul principal Contor = 1 Indice = 1

In programul principal Contor = 2 Indice = 2In Afiseaza_Date Contor = 7 Index = 2In programul principal Contor = 2 Indice = 2

In programul principal Contor = 3 Indice = 3In Afiseaza_Date Contor = 7 Index = 3In programul principal Contor = 3 Indice = 3

Programul s-a terminat}

16

Page 17: Sub Program Are

Variabila Contor este declarata de doua ori: odata Œn zona de declaratii a programului principal (de unde va rezulta ca este globala) si Œnca odata Œn zona de declaratii a procedurii Afiseaza_Date, ceea ce Œnseamna ca este locala Œn respectiva procedura. Daca analizam DoV al variabilei Contor globala acesta tine de la declaratia sa si pƒna la sfƒrsitul programului principal, mai putin procedura Afiseaza_Date, care o redeclara. Aceasta Œnseamna ca- Œn tot corpul programului principal si al subprogramelor declarate dupa declaratia variabilei globale Contor (cu exceptia blocului procedurii Afiseaza_Date) numele Contor se refera la variabila globala;- Œn blocul procedurii Afiseaza_Date, numele Contor se refera la variabila locala, declarata acolo;Executia programului justifica afirmatiile de mai sus. Tehnic vorbind, redeclararea unui identificator Œn DoV al sau are ca efect introducerea unei 'gauri' Œn acesta; blocul de redeclarare va folosi numele redeclarat Œn acceptiunea cea mai recent Œntƒlnita. Acesta este motivul pentru care se obtin valori diferite pentru variabila Contor Œn programul principal si Œn procedura Afiseaza_Date: este vorba de fapt de doua variabile diferite, cu domenii de vizibilitate disjuncte (si cu durate de viata diferite) si careau prin urmare adrese diferite Œn memorie, deci valori diferite.Situatia opusa este atunci cƒnd doua variabile diferite au acceasi adresa de memorie si (daca sunt de acelasi tip) aceeasi valoare, care se modifica simultan pentru ambele. Este situatia descrisa mai Œnainte a parametrilor actuali transmisi prin referinta: atƒt parametrul formal, cƒt si parametrul actual (care sunt doua variabile diferite, cu toate atributele ce le definesc) au aceeasi adresa din memorie si trebuie sa aiba acelasi tip de date (altfel apelul n-ar fi corect).

8.6.5. Reguli de proiectare a subprogramelorVom da Œn continuare cƒteva reguli privind proiectarea subprogramelor.Aceste reguli privesc declararea acestora (am vazut anterior ca declararea este cea care dicteaza modul de apelare). Ne vom referi la:- sarcinile realizate de subprogram- stabilirea parametrilor subprogramului si a modului de transmitere a acestora- alegerea tipului de subprogram (procedura sau functie)- stabilirea numelui subprogramului si a parametrilor acestuiaOrice subprogram trebuie sa realizeze o actiune bine determinata (calculul unui determinant, determinarea minimului unui sir de numere, rezolvarea unui sistem de ecuatii, afisarea unui meniu, etc). Se spune ca subprogramul este bine gƒndit daca actiunea realizata de el se poate concentra Œn numeleacestuia.Urmatoarea actiune care trebuie Œntreprinsa este stabilirea parametrilor de intrare si de iesire. Daca ne gƒndim ca un subprogram este implementarea unui subalgoritm, atunci datele de intrare ale subalgoritmului vor fi parametrii de intrare, iar rezultatele algoritmului vor fi parametrii de iesire.Parametrii de intrare se transmit de obicei prin valoare (cu unele exceptii care le vom discuta la vremea lor), iar parametrii de iesire se transmit Œntotdeauna prin referinta.Alegerea tipului subprogramului (procedura sau functie) este Œn strƒnsa legatura cu actiunea efectuata si cu numarul de parametri de iesire ai subprogramului. Daca subprogramul are un singur parametru de iesire si acesta este de un tip simplu de data, atunci subprogramul va fi de tip functie. De exemplu: calculul minimului dintr-un sir sau calculul de determinant se pot implementa ca subprograme de tip functie. Chiar si afisarea unui meniu se poate implementa ca functie, daca Œnsotim afisarea de selectarea unei optiuni din meniu (caz Œn care functia va Œntoarce codul optiunii selectate). Daca Œnsa subprogramul are mai multi parametri de iesire sau daca tipul parametrului de iesire este unul structurat, atunci el va fi proiectat ca procedura.Atƒt numele subprogramului, cƒt si numele parametrilor sai trebuie sa fie sugestive. Numele subprogramului procedura va desemna o actiune, pe cƒnd numele subprogramului functie poate desemna obiectul (valoarea) Œntoarsa.Una din deosebirile dintre proceduri si functii, enumerate Œn 8.5 s-a referit la restrictia aplicata functiilor de a nu avea parametri de iesire. Daca o instructiune din corpul unui subprogram de tip functie modifica valoarea unei variabile globale (accesata direct sau printr-un parametru transmis prin referinta),

17

Page 18: Sub Program Are

se spune ca functia are un 'efect secundar' sau 'efect colateral'. Acest efect se numeste 'secundar' deoarece efectul 'primar' al functiei este calculul unei valori, care este Œntoarsa de aceasta Œn programul apelant. De aceea, unele limbaje (de exemplu Ada; Pascal nu) impun ca toti parametrii unei functii sa se transmita prin valoare (sau prin constanta, o varianta a apelului prin valoare care interzice modificarea valorilor parametrilor formali Œn corpul subprogramului). Folosirea efectelor secundare este proprie programatorilor experimentati, Œnsa Œn general nu se recomanda.Regulile prezentate aici vin sa completeze sfaturile pe care le-am enuntat la sfƒrsitul sectiunii 8.3 privitoare la rolul subprogramelor Œntr-un program.

8.7. Subprograme apelate din subprograme

Din punctul de vedere al drepturilor sale de apel, un subprogram SP secomporta exact ca un program principal P; la rƒndul sau, SP poate avea Œncorpul sau instructiuni de apel ale altor subprograme, inclusiv apelulpropriu. Singura conditie impusa este aceea ca subprogramele apelate de SP saincluda Œn domeniul lor de vizibilitate corpul subprogramului SP.

8.7.1. Apelul subprogramelor din subprograme. Subprograme locale Œn subprograme

Sa consideram urmatorul exemplu:

Program P4a;{ Apelul unei proceduri din alta procedura }

Procedure Unu; Begin Writeln('Aceasta este procedura Unu'); End; { Unu }

Procedure Doi; Begin Unu; Writeln('Aceasta este procedura Doi'); End; { Doi }

Procedure Trei; Begin Doi; Writeln('Aceasta este procedura Trei'); End; { Trei }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

begin { Programul principal } Unu; Writeln; Doi; Writeln;

18

Page 19: Sub Program Are

Trei; Scrie_Sfarsit end. { P4a }

{ Rezultatul executiei

Aceasta este procedura Unu

Aceasta este procedura UnuAceasta este procedura Doi

Aceasta este procedura UnuAceasta este procedura DoiAceasta este procedura TreiProgramul s-a terminat}Programul P4a este un prim exemplu de apel de procedura Œn procedura.Procedura Doi contine Œn corpul ei apelul procedurii Unu, iar procedura Treicontine Œn corpul ei apelul procedurii Doi. Rezultatul apelului acestorproceduri Œn programul principal este ilustrat Œn exemplul de mai sus.Analizƒnd structura acestui program:Program P4a;[declaratii] Procedure Unu; Procedure Doi; Procedure Trei; Procedure Scrie_Sfarsit; begin { Programul principal } [corp] end. { P4a }

se observa ca declaratiile procedurilor Unu, Doi, Trei, Scrie_Sfarsit sunt continute (Œn aceasta ordine) Œn partea de declaratii a sa. De aici rezulta ca:- DoV al numelui Unu contine declaratiile procedurilor Unu, Doi, Trei, Scrie_Sfarsit, precum si programul principal, deci procedura Unu va putea fi apelata Œn oricare dintre acestea (inclusiv Œn Unu - vezi paragraful urmator);- DoV al numelui Doi contine declaratiile procedurilor Doi, Trei, Scrie_Sfarsit, precum si programul principal, deci procedura Doi va putea fi apelata Œn oricare dintre acestea, mai putin Œn Unu - care nu face parte din DoV al lui Doi- DoV al numelui Trei contine declaratiile procedurilor Trei, Scrie_Sfarsit, precum si programul principal, deci procedura Trei va putea fi apelata Œn oricare dintre acestea, mai putin Œn Unu si Doi - care nu fac parte din DoV al lui Trei- s.a.m.d.

Urmatorul exemplu vine sa ilustreze generalitatea conceptului de bloc dinPascal. Cum zona de declaratii a unui subprogram poate contine oricedeclaratie valida, ea va putea contine si declaratii de subprograme. Inprogramul P4b am inclus declaratia procedurii Doi Œn zona de declaratii aprocedurii Unu si am inversat apelurile: Unu apeleaza pe Doi. Ce constatamdaca dorim sa lansam Œn executie acest program este ca el nu functioneaza:se produce o eroare de compilare:

19

Page 20: Sub Program Are

Unknown identifier - Identificator necunoscut

la apelul procedurii Doi Œn corpul lui Trei si (daca comentam acest apel) laapelul lui Doi din programul principal.

Program P4b;{ Apelul unei proceduri din alta procedura }

Procedure Unu; Procedure Doi; Begin Writeln('Aceasta este procedura Doi din Unu'); End; { Doi }

Begin { Unu } Writeln('Aceasta este procedura Unu'); Doi; { corect! Doi este locala in Unu } End; { Unu }

Procedure Trei; Begin Doi; { eroare de compilare: Identificator necunoscut } Writeln('Aceasta este procedura Trei'); End; { Trei }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

begin { Programul principal } Unu; Writeln; Doi; { eroare de compilare: Identificator necunoscut } Writeln; Trei; { nu se poate compila Trei } Scrie_Sfarsit end. { P4b }

{ Rezultatul executiei: programul are erori de compilare }

Structura programului P4b se Œnscrie Œn urmatoarea schema:

Program P4b;[declaratii] Procedure Unu; Procedure Doi; { locala Œn Unu si invizibila Œn afara } Procedure Trei; Procedure Scrie_Sfarsit; begin { Programul principal } [corp] end. { P4b }

20

Page 21: Sub Program Are

din care se observa ca procedurile locale Œn program sunt procedurilor Unu,Trei, Scrie_Sfarsit care sunt continute (Œn aceasta ordine) Œn partea dedeclaratii a sa, iar procedura Doi este locala Œn Unu. De aici rezulta ca:- DoV al numelui Unu contine declaratiile procedurilor Unu, Doi, Trei, Scrie_Sfarsit, precum si programul principal, deci procedura Unu va putea fi apelata Œn oricare dintre acestea (inclusiv Œn Unu - vezi paragraful urmator);- DoV al numelui Doi contine doar declaratiile procedurilor Doi si Unu, fara programul principal, deci procedura Doi nu va putea fi apelata decƒt Œn corpul lui Unu - unde este locala si Œn propriul sau corp (apel recursiv - vezi sectiunea urmatoare)- DoV al numelui Trei contine declaratiile procedurilor Trei, Scrie_Sfarsit, precum si programul principal, deci procedura Doi va putea fi apelata Œn oricare dintre acestea, mai putin Œn Unu si Doi - care nu fac parte din DoV al sau- s.a.m.d.

Ultimul exemplu care-l dam aici reproduce functionalitatea programului P4afolosind proceduri declarate local Œn alte proceduri:

program P4c;{ Apelul unei proceduri din alta procedura }

Procedure Unu; Begin Writeln('Aceasta este procedura Unu'); End; { Unu }

Procedure Doi; Procedure Unu; Begin Writeln('Aceasta este procedura Unu din Doi'); End; { Unu } Begin { Doi } Unu; { apelul procedurii locale Unu } Writeln('Aceasta este procedura Doi'); End; { Doi }

Procedure Trei; Procedure Doi; Procedure Unu; Begin Writeln('Aceasta este procedura Unu din Doi din Trei'); End; { Unu } Begin { Doi } Unu; { apelul procedurii locale Unu } Writeln('Aceasta este procedura Doi din Trei'); End; { Doi } Begin { Trei } Doi; { apelul procedurii locale Doi } Writeln('Aceasta este procedura Trei');

21

Page 22: Sub Program Are

End; { Trei }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

begin { Programul principal } Unu; { apelul procedurii Unu din programul principal } Writeln; Doi; Writeln; Trei; Scrie_Sfarsit end. { P4c }

{ Rezultatul executiei

Aceasta este procedura Unu

Aceasta este procedura Unu din DoiAceasta este procedura Doi

Aceasta este procedura Unu din Doi din TreiAceasta este procedura Doi din TreiAceasta este procedura TreiProgramul s-a terminat}

Structura acestui program este urmatoarea

Program P4c;[declaratii] Procedure Unu; Procedure Doi; Procedure Unu; { locala Œn Doi si invizibila Œn afara } Procedure Trei; Procedure Doi; { locala Œn Trei si invizibila Œn afara } Procedure Unu; { locala Œn Doi si invizibila Œn afara } Procedure Scrie_Sfarsit; begin { Programul principal } [corp] end. { P4c }

iar functionarea programului (ilustrata de rezultatul tiparit) estejustificata doar de regulile de vizibilitate a identificatorilor Œntr-unprogram cu structura de bloc. Sa remarcam aici ca declaratiile procedurilorlocale introduc 'gauri' Œn DoV al identificatorilor cu acelasi numedeclarati Œn programul principal si ca Œn corpul procedurilor Doi si Trei vorfi apelate, din acest motiv, subprogramele locale acestora si nu cele globale.Mai mult, acest program va functiona corect indiferent de ordinea Œn careapar declaratiile procedurilor Unu, Doi si Trei Œn zona de declaratii a

22

Page 23: Sub Program Are

programului principal (ceea ce nu se va Œntƒmpla Œn P4a - acolo s-ar produceo eroare de compilare). Sa remarcam de asemenea ca Œn P4c se folosescidentificatori redefiniti fara a se provoca o eroare de compilare. Fiecareidentificator are propriul sau DoV si nu exista ambiguitati.

8.7.2. Apelul recursiv

In paragraful precedent am amintit ca numele unei proceduri se poate referiŒn corpul acesteia. Am discutat de asemenea Œn 8.5 faptul ca numele uneifunctii este obligatoriu sa apara Œn membrul stƒng al unei operatii deatribuire. Ce se Œntƒmpla Œnsa daca Œn corpul procedurii P apare oinstructiune de apel a sa, sau daca Œn corpul functiei F numele acesteiaapare Œn membrul drept al unei operatii de atribuire? Ambele situatii suntpermise (Œn anumite conditii care vor fi discutate Œn continuare) si sunttratate ca apeluri (de procedura, respectiv de functie), fiind numite'apeluri recursive'.

Subprogramele recursive permit o descriere eleganta a proceselor de calcul(algoritmilor) care se preteaza gƒndirii recursive. De fapt recursivitateaeste o alta metoda de reducere a unei probleme date P la o problema maisimpla. Spre deosebire de descompunerea functionala, Œn care subproblemelesunt diferite de problema initiala, Œn cazul recursivitatii simple rezolvareaproblemei P (de dimensiune n) se reduce la rezolvarea aceleiasi probleme P(de dimensiune n-1 sau mai mica).

Un exemplu simplu de aplicare a recursivitatii este determinarea factorialuluidin numarul natural n:

Fact(n) ::= n * Fact(n-1);

care nu inseamna decat aplicarea formulei de recurenta

n! = n * (n-1)!

Din punctul de vedere al analizei noastre, am redus problema determinarii luin! (care spunem ca are dimensiunea n) la problema determinarii lui (n-1)!(deci aceeasi problema, dar de dimensiune n-1).

Din schema de mai sus se observa ca problema nu este Œn Œntregime rezolvata.Pe lƒnga stabilirea unei legaturi Œntre P(n) si P(n-1), trebuie obligatoriugasit un criteriu de stop (similar criteriului de oprire a descompunerii):cea mai mare valoare a lui n pentru care P(n) are solutie cunoscuta si decinu mai trebuie aplicata recursivitatea.

Prin urmare, rezolvarea recursiva a unei probleme P(n) Œnseamna stabilirea adoua elemente, la fel de importante:- o relatie Œntre P(n) si P(n-1)- un criteriu de oprire (o valoare a lui n pentru care P(n) are solutie cunoscuta) - Œn cazul factorialului, criteriul de stop se poate considera n = 1, avƒnd Œn vedere ca 1! = 1 este cunoscut (din definitia factorialului)

23

Page 24: Sub Program Are

Un alt exemplu simplu de aplicare a recursivitatii este determinareaminimului unui sir de numere a1, a2, ..., an. Plecam de la urmatoareaconstatare:

* n = 2: P(2) Œnseamna determinarea minimului a doua numere, a1, a2 si se considera ca are solutie (se poate implementa direct)

Minim(a1, a2) ::= a1 daca a1 < a2 , respectiv a2 daca a2 = a1

* n > 2: P(n) se rezolva astfel:

Minim(a1, a2, ..., an) ::= Minim(a1, Minim(a2, ..., an))

Criteriul de stop este aici atingerea valorii n = 2.

Atƒt procedurile, cƒt si functiile se pot apela recursiv. Exemplele urmatoareilustreaza acest lucru.

program P5;{ Proceduri recursive } Uses Crt;

Procedure Afiseaza_Titlu; Begin ClrScr; WriteLn('Program care foloseste o procedura recursiva'); End; { AfiseazaTitlu }

Procedure Afiseaza_Si_Micsoreaza(Indice : integer); Begin Writeln('Valoarea indicelui este ',Indice:3); Indice := Indice - 1; If Indice > 0 Then Afiseaza_Si_Micsoreaza(Indice); End; { Afiseaza_Si_Micsoreaza }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

Var Contor: Integer;

Begin { programul principal } Afiseaza_Titlu; Contor := 7; Afiseaza_Si_Micsoreaza(Contor); Scrie_Sfarsit End. { P5 }

{ Rezultatul executiei

24

Page 25: Sub Program Are

Program care foloseste o procedura recursivaValoarea indicelui este 7Valoarea indicelui este 6Valoarea indicelui este 5Valoarea indicelui este 4Valoarea indicelui este 3Valoarea indicelui este 2Valoarea indicelui este 1Programul s-a terminat

}

Procedura recursiva din acest program este Afiseaza_Si_Micsoreaza. Ea primesteca parametru (transmis prin valoare) un Œntreg Indice care este afisat(valoarea cu care s-a facut apelul), apoi este micsorat si (daca valoarea saeste strict pozitiva) se apeleaza din nou procedura (cu noua valoare a luiIndice). Se observa si din modul de executie a programului ca la urmatoareaexecutie se repeta aceleasi operatii. Criteriul de stop al apelului recursiveste atingerea valorii 0 pentru Indice. Se observa ca apelul recursiv trebuiesa respecte Œntr-u totul sintaxa uzuala de apel a procedurii.

Al doilea exemplu de program din acest paragraf ilustreaza folosireafunctiilor recursive. Reamintim ca apelul functiei este diferit sintactic sisemantic de apelul de procedura.

program P6;{ Functii recursive } Uses Crt;

Procedure Afiseaza_Titlu; Begin ClrScr; WriteLn('Program care determina CMMDC a doua numere'); End; { AfiseazaTitlu }

Function CMMDC_Rec(a, b: LongInt): LongInt; { determina cmmdc al numerelor a si b folosind formula: cmmdc(a, b) = cmmdc(b, a mod b) daca b <> 0 = a daca b = 0 Preconditie: a > b > 0 } Begin WriteLn('CMMDC(',a:11, ',', b:11, ')='); If b = 0 then CMMDC_Rec := a Else CMMDC_Rec := CMMDC_Rec(b, a mod b); End; { CMMDC_Rec }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

25

Page 26: Sub Program Are

Var Numar1: LongInt; { primul numar citit } Numar2: LongInt; { al doilea numar citit }

Begin { Programul principal } Afiseaza_Titlu; Write('Introduceti primul numar: '); ReadLn(Numar1); Write('Introduceti al doilea numar: '); ReadLn(Numar2); If Numar1 >= Numar2 Then WriteLn(CMMDC_Rec(Numar1, Numar2)) Else WriteLn(CMMDC_Rec(Numar2, Numar1)); Scrie_Sfarsit End. { P6 }

{ Rezultatul executiei

Program care determina CMMDC a doua numereIntroduceti primul numar: 1235Introduceti al doilea numar: 65700CMMDC( 65700, 1235) =CMMDC( 1235, 245) =CMMDC( 245, 10) =CMMDC( 10, 5) =CMMDC( 5, 0) =5Programul s-a terminat

}

Acest exemplu implementeaza o varianta recursiva a algoritmului de determinarea celui mai mare divizor comun a doua numere naturale, folosind relatia derecurenta enuntata Œn comentariile aferente functiei CMMDC_Rec. Conformƒndu-neregulilor enuntate anterior, deoarece subprogramul de determinare a cmmdcprimeste ca parametri doua numere Œntregi si Œntoarce un alt numar, estenatural sa fie conceput ca functie. Criteriul de stop al recursivitatii estedat de relatia

cmmdc(a, 0) ::= a

iar reducerea problemei la una de dimensiune mai mica (dimensiune mai micaŒnseamna aici parametri cu valori mai mici) se bazeaza pe relatia amintita:

cmmdc(a, b) ::= cmmdc(b, a mod b) (Preconditie: a >= b)

Verificarea preconditiei se face Œn programul apelant (programul principal),dupa citirea valorilor numerelor pentru care se calculeaza cmmdc, rezultƒnddoua apeluri Œnglobate Œntr-o structura alternativa. Corpul subprogramuluiafiseaza prima data parametrii de apel (pentru a se ilustra succesiuneaapelurilor si deci maniera de rezolvare a problemei). Sa mai observam carelatia de recurenta enuntata conserva preconditia (Œntotdeauna b > a mod b).

26

Page 27: Sub Program Are

Un algoritm mai lent este cel Œn care relatia de recurenta este

cmmdc(a, b) ::= cmmdc(b, a - b)

care Œnsa nu mai pastreaza preconditia. Sugeram cititorului implementarea lui.

8.7.3. Subprograme mutual recursive

Un caz interesant de recursivitate este acela Œn care sunt implicate douasubprograme, A si B si Œn care- A apeleaza Œn corpul sau pe B- B apeleaza Œn corpul sau pe A

si oricare dintre ele se pot apela din programul principal. Subprogramele Asi B care Œndeplinesc aceste conditii se numesc 'mutual recursive'.

Cu ceea ce stim pƒna acum despre Pascal, acest lucru este imposibil. Sa vedemde ce.

Consideram ca A si B sunt subprograme declarate local Œn programul P.Declararea lor ar putea fi Œn ordinea A, B sau B, A. In ambele situatii,apelul reciproc nu se poate implementa. De exemplu, ordinea

Program P; Procedure A; [corp A] {contine apelul lui B - eroare! B nedeclarat Œnca} Procedure B; [corp B] {contine apelul lui A} Begin A; B End.

ar provoca o eroare de compilare (Œn corpul lui A se apeleaza B, care nu afost Œnca declarat, deci se obtine eroarea de compilare

Unknown identifier = Identificator necunoscut

Analog, daca se inverseaza ordinea de declarare, eroarea de compilare s-arproduce Œn corpul lui B.

Ar fi o solutie care ar rezolva partial problema (A si B s-ar putea apelareciproc), Œnsa numai una dintre ele ar putea fi apelata din programulprincipal:

Program P; Procedure A; Procedure B; { locala Œn A } [corp B] {contine apelul lui A - corect! A este declarat} [corp A] {contine apelul lui B - corect! B este declarat} Begin

27

Page 28: Sub Program Are

A; B {eroare de compilare! B nu este vizibil aici} End.

Pentru a rezolva complet problema, este nevoie de Œnca un mecanism. Acestaeste concretizat Œn Turbo Pascal de directiva 'Forward', care are ca scop saanunte compilatorului sintaxa de apel a unui subprogram care va fi declaratmai tƒrziu.

Sintaxa acestei directive este

Antet subprogram; Forward;

si se aplica ca Œn exemplul urmator

Program P; Procedure B(parametri); Forward; Procedure A(parametri); [corp A] {contine apelul lui B - corect! apelul lui B este precizat Œn Forward} Procedure B;{nu mai este obligatorie precizarea parametrilor} [corp B] {contine apelul lui A} Begin A; B End.

Este valabila si versiunea:

Program P; Procedure A(parametri); Forward; Procedure B(parametri); [corp B] {contine apelul lui A - corect! apelul lui A este precizat Œn Forward} Procedure A;{nu mai este obligatorie precizarea parametrilor} [corp A] {contine apelul lui B} Begin A; B End.

Exemplul prezentat Œn continuare exemplifica declararea si folosireaprocedurilor mutual recursive. Sa remarcam ca cel putin una dintre proceduritrebuie sa aiba Œn corpul ei o instructiune de stop - altfel s-ar ajunge laun ciclu infinit.

program P7;{ Proceduri mutual recursive } Uses Crt;

Procedure Afiseaza_Titlu; Begin

28

Page 29: Sub Program Are

ClrScr; WriteLn('Apel recursiv - proceduri mutual recursive'); End; { AfiseazaTitlu }

Procedure Scrie_O_Linie(var Contor : Integer); Forward;

Procedure Micsoreaza(var Indice : Integer); Begin Indice := Indice - 1; If Indice > 0 Then Scrie_O_Linie(Indice); End; { Micsoreaza }

Procedure Scrie_O_Linie; begin Writeln('Valoarea contorului este acum ',Contor:4); Micsoreaza(Contor); end; { Scrie_O_Linie }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

Var De_Cate_Ori : integer;

Begin { programul principal } Afiseaza_Titlu; De_Cate_Ori := 7; WriteLn('Apelez Micsoreaza'); Micsoreaza(De_Cate_Ori); Writeln; De_Cate_Ori := 7; WriteLn('Apelez Scrie_O_Linie'); Scrie_O_Linie(De_Cate_Ori); Scrie_Sfarsit End. { P7 }

{ Rezultatul executiei

Apel recursiv - proceduri mutual recursiveApelez MicsoreazaValoarea contorului este acum 6Valoarea contorului este acum 5Valoarea contorului este acum 4Valoarea contorului este acum 3Valoarea contorului este acum 2Valoarea contorului este acum 1

Apelez Scrie_O_LinieValoarea contorului este acum 7

29

Page 30: Sub Program Are

Valoarea contorului este acum 6Valoarea contorului este acum 5Valoarea contorului este acum 4Valoarea contorului este acum 3Valoarea contorului este acum 2Valoarea contorului este acum 1Programul s-a terminat

}

8.8. Tipuri procedurale

Incepƒnd cu versiunea 5.0, mediul Turbo Pascal poseda doua tipuri de date noi,numite generic 'tipuri procedurale': tipul Procedure si tipul Function.Aceste tipuri se pot declara de catre utilizator si se includ Œn sistemul detipuri al limbajului. Prin urmare, se pot declara si folosi variabile detipurile respective.

Sintaxa declaratiei tipurilor procedurale este:

Type Nume_Tip_Proc = Procedure(lista_parametri_formali); Nume_Tip_Func = Function(lista_parametri_formali): tip_rezultat;

unde lista_parametri_formali si tip_rezultat au semnificatia de la declarareaprocedurilor si functiilor.

Rostul declaratiei de tip procedural este precizarea signaturii tipului desubprogram, anume:- precizarea tipurilor parametrilor formali (si a modului de transmitere) - nu este obligatoriu ca numele parametrilor sa coincida - perechile (tip_parametru, mod de transmitere) pentru fiecare parametru determina ceea ce numim 'signatura' tipului procedural - perechile (tip_parametru, mod de transmitere) pentru fiecare parametru, impreuna cu tipul rezultatului intors determina ceea ce numim 'signatura' tipului functional- precizarea tipului rezultatului Œntors (numai pentru tipurile functionale)

Pentru ca sa se poata folosi tipuri procedurale Œntr-un program, acestatrebuie sa contina o directiva de compilare {$F+} (Force Far Calls).

8.8.1. Tipuri Procedure

Numele oricarui subprogram de tip Procedure care are signatura identica cusignatura tipului Nume_Tip_Proc va putea fi atribuit unei variabile de tipulNume_Tip_Proc si orice variabila de acest tip va putea Œnlocui Œntr-oinstructiune de apel numele concret al subprogramului continut de ea.

Exemplul urmator este sugestiv:

program P8a;{ Tipuri procedurale }

30

Page 31: Sub Program Are

Uses Crt;

{$F+} { Aceasta directiva forteaza apelurile far si este necesara cand dorim sa lucram cu tipuri procedurale in Turbo Pascal }

Type Op_Aritm = Procedure(a, b, c : Integer; var r : Integer); { tip procedural }

Procedure Afiseaza_Titlu; Begin ClrScr; WriteLn('Program care foloseste tipuri procedurale si'); WriteLn('efectueaza operatii aritmetice cu trei numere') End; { AfiseazaTitlu }

Procedure Adunare(In1, In2, In3: Integer; var Rezultat : Integer); Begin Rezultat := In1 + In2 + In3; Writeln('Suma numerelor este ', Rezultat:6); End; { Adunare }

Procedure Inmultire(In1, In2, In3: Integer; var Rezultat : Integer); Begin Rezultat := In1 * In2 * In3; Writeln('Produsul numerelor este ', Rezultat:6); End; { Inmultire }

Procedure Medie(In1, In2, In3: Integer; var Rezultat : Integer); Begin Rezultat := (In1 + In2 + In3) div 3; Writeln('Media numerelor este ', Rezultat:6); End; { Medie }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

Var Numar1, Numar2, Numar3: Integer; Rezultat_Final: Integer; Operatie: Op_Aritm;

Begin { Programul principal }

Afiseaza_Titlu; Write('Introduceti primul numar: '); ReadLn(Numar1);

31

Page 32: Sub Program Are

Write('Introduceti al doilea numar: '); ReadLn(Numar2); Write('Introduceti al treilea numar: '); ReadLn(Numar3);

Operatie := Adunare; Operatie(Numar1, Numar2, Numar3, Rezultat_Final);

Operatie := Inmultire; Operatie(Numar1, Numar2, Numar3, Rezultat_Final);

Operatie := Medie; Operatie(Numar1, Numar2, Numar3, Rezultat_Final);

Scrie_Sfarsit end. { P8a }

{ Rezultatul executiei

Program care foloseste tipuri procedurale siefectueaza operatii aritmetice cu trei numereIntroduceti primul numar: 12Introduceti al doilea numar: 23Introduceti al treilea numar: 34Suma numerelor este 69Produsul numerelor este 9384Media numerelor este 23Programul s-a terminat

}

Programul de mai sus defineste tipul procedural Op_Aritm, care are trei parametri Œntregi de intrare (transmisi prin valoare) si unul Œntreg de iesire (transmis prin adresa). In partea sa de declaratii sunt definite si trei proceduri care efectueaza operatii aritmetice (numite Adunare, Inmultire si Medie) care au aceeasi signatura ca si Op_Aritm (chiar daca numele parametrilor difera, numarul lor este acelasi, si ei corespund ca tip si ca modalitate de transmitere). Zona de declaratii de variabile a programului contine declararea unei variabile procedurale (de tipul Op_Aritm) numita Operatie, care va fi folosita Œn corpul programului pentru apelul indirect al procedurilor aritmetice precizate anterior.

De exemplu, maniera de apelare a procedurii Adunare este urmatoarea:

Operatie := Adunare; Operatie(Numar1, Numar2, Numar3, Rezultat_Final);

Prima instructiune initializeaza variabila Operatie cu adresa proceduriiAdunare. Initializarea este posibila deoarece Adunare si Operatie au aceeasisignatura (si deci acelasi tip de date).

A doua instructiune se traduce printr-un apel indirect de procedura. VariabilaOperatie contine adresa procedurii Adunare si prin urmare acest apel setraduce de fapt Œn apelul

32

Page 33: Sub Program Are

Adunare(Numar1, Numar2, Numar3, Rezultat_Final);

8.8.2. Tipuri FunctionNumele oricarui subprogram de tip Function care are signatura identica cu signatura tipului Nume_Tip_Func va putea fi atribuit unei variabile de tipulNume_Tip_Func si orice variabila de acest tip va putea Œnlocui Œntr-o instructiune de apel numele concret al subprogramului continut de ea.Exemplul urmator este echivalent cu cel de la tipuri procedurale, folosind de data aceasta tipuri functionale (care sunt mai naturale pentru cazul ilustrat, deoarece operatiile aritmetice prezentate se implementeaza natural ca functii):

program P8b;{ Tipuri functionale } Uses Crt;

{$F+} { Aceasta directiva forteaza apelurile far si este necesara cand dorim sa lucram cu tipuri procedurale sau functional in Turbo Pascal }

Type Op_Aritm = Function(a, b, c : integer): Integer;

Procedure Afiseaza_Titlu; Begin ClrScr; WriteLn('Program care foloseste tipuri functionale si'); WriteLn('efectueaza operatii aritmetice cu trei numere') End; { AfiseazaTitlu }

Function Adunare(In1, In2, In3: Integer): Integer; Var R: Integer; Begin R := In1 + In2 + In3; Writeln('Suma numerelor este ', R:6); Adunare := R End; { Adunare }

Function Inmultire(In1, In2, In3: Integer): Integer; Var R: Integer; Begin R := In1 * In2 * In3; Writeln('Produsul numerelor este ', R:6); Inmultire := R End; { Inmultire }

Function Medie(In1, In2, In3: Integer): Integer; Var R: Integer; Begin R := (In1 + In2 + In3) div 3; Writeln('Media numerelor este ', R:6);

33

Page 34: Sub Program Are

Medie := R End; { Medie }

Procedure Scrie_Sfarsit; Begin Writeln('Programul s-a terminat'); End; { Scrie_Sfarsit }

Var Numar1, Numar2, Numar3: Integer; Rezultat_Final: Integer; Operatie: Op_Aritm;

Begin { Programul principal }

Afiseaza_Titlu; Write('Introduceti primul numar: '); ReadLn(Numar1); Write('Introduceti al doilea numar: '); ReadLn(Numar2); Write('Introduceti al treilea numar: '); ReadLn(Numar3);

Operatie := Adunare; Rezultat_Final := Operatie(Numar1, Numar2, Numar3);

Operatie := Inmultire; Rezultat_Final := Operatie(Numar1, Numar2, Numar3);

Operatie := Medie; Rezultat_Final := Operatie(Numar1, Numar2, Numar3);

Scrie_Sfarsit end. { P8b }{ Rezultatul executieiProgram care foloseste tipuri procedurale siefectueaza operatii aritmetice cu trei numereIntroduceti primul numar: 12Introduceti al doilea numar: 23Introduceti al treilea numar: 34Suma numerelor este 69Produsul numerelor este 9384Media numerelor este 23Programul s-a terminat}Sa remarcam ca s-au modificat corespunzator declaratiile tipului procedural (functional) Op_Aritm, ale subprogramelor aritmetice (care sunt acum functii) si apelul acestora din programul principal.

1. Generalitati2. Elemente de sintaxa3. Structura unui program4. Tipuri de date predefinite

34

Page 35: Sub Program Are

5. Expresii6. Declaratii si definitii7. Instructiuni8. Subprograme Pascal9. Tipuri de date structurate10. Structuri dinamice de date11. Elemente de modularizare12. Abstractizarea datelor13. Programarea orientata pe obiecte

Capitolul 9. Tipuri de date structurate

9.1. Tipul de date tablou9.1.1. Conceptul de tablou9.1.2. Declararea tablourilor9.1.3. Accesarea tablourilor9.1.4. Tablouri bidimensionale9.1.5. Tablouri multidimensionale9.1.6. Operatii globale pe tablori9.1.7. Stringuri9.1.8. Tablouri deschise

9.2. Tipul de date multime9.2.1. Definitia tipurilor multime9.2.2. Construirea multimilor si atribuirea9.2.3. Compararea multimilor si testul de apartenenta9.2.4. Operatii cu multimi

9.3. Tipul de date Œnregistrare9.3.1. Conceptul de Œnregistrare9.3.2. Declararea tipului Œnregistrare9.3.3. Accesarea componentelor. Instructiunea With9.3.4. Structuri complexe9.3.5. Inregistrari cu variante9.3.6. Initializarea Œnregistrarilor

9.4. Tipul de date fisier9.4.1. Notiunea de fisier9.4.2. Operatii asupra fisierelor9.4.3. Fisiere text9.4.4. Fisiere cu tip9.4.5. Fisiere fara tip (stream)9.4.6. Fisierele sistem

Tipuri de date structurate

Limbajele de programare dispun de modalitati de agregare a datelor carepermit apoi tratarea globala a acestora. Este vorba Œn general de date carecorespund nivelului de abstractizare al limbajului, deci care nu aucorespondent direct Œn tipurile masina. Pentru ca aceste date definite deutilizator conform nevoilor sale concrete sa poata fi integrate Œn mecanismulde tipuri al limbajului, acesta din urma pune la dispozitia programatorului

35

Page 36: Sub Program Are

constructorii de tipuri. In acest capitol se vor discuta tipurile de datestructurate.

Spre deosebire de datele simple, care sunt atomice, indivizibile, datelestructurate (compuse, agregate) se descompun Œn componente sau elemente,fiecare de un tip precizat (simplu sau structurat). O data structurata poatefi accesata fie ca Œntreg (global), fie pe componente. Structura unei datestabileste relatiile care exista Œntre componentele acesteia.

Exista patru tipuri de legaturi structurale fundamentale: - multime (nici o legatura Œntre componente), - liniara (legatura 1:1), - arbore (legatura 1:n) si - graf (legatura m:n).

Din punctul de vedere al uniformitatii structurale, datele structurate seŒmpart Œn:- omogene (toate componentele au acelasi tip); tipurile de date aferente sunt numite tablou (engl. array), multime (engl. set) si fisier (engl. file);- heterogene (elementele unei date au de obicei componente diferite ca tip); ele apartin tipului de date Œnregistrare (engl. record).

Tablourile, fisierele si Œnregistrarile au structura liniara: exista o primasi o ultima componenta, iar toate celelalte au fiecare atƒt predecesor, cƒt sisuccesor. Prin urmare, un element (al tabloului), o Œnregistrare (din fisier)sau un cƒmp (al Œnregistrarii) se pot localiza. Un tablou este un agregat deelemente de acelasi tip, un element fiind localizat prin pozitia pe care oocupa Œn cadrul acestuia (indicele elementului de tablou). Un fisier esteconstituit si el din elemente (Œnregistrari) de acelasi tip, localizate totdupa pozitia ocupata Œn fisier. Deosebirea dintre un tablou si un fisierconsta Œn aceea ca tabloul este memorat Œn memoria interna a calculatorului,iar fisierul este memorat Œn memoria externa (pe un suport magnetic saumagneto-optic). O Œnregistrare este un agregat care grupeaza de obiceielemente de tipuri diferite numite cƒmpuri si localizate prin numele lor.Multimea are o structura amorfa: ea contine elemente de acelasi tip, careŒnsa nu pot fi localizate explicit, neexistƒnd o ordine in care sa fieconsiderate elementele din ea.

Pentru tipurile structurate, exista doua operatii de baza: construirea siselectarea componentelor. Operatia de construire a unei variabile de tipstructurat se face dupa regulile proprii ale fiecarui limbaj. Pentru tablouri,fisiere si Œnregistrari exista si operatia de selectare a unei componente,care este realizata Œn maniere diferite.

In cazul unui tablou, selectarea unui element se face pe baza unei expresiide indice, atasata numelui variabilei tablou. Pe baza expresiei de indice sia informatiilor despre tablou se efectueaza calculul adresei elementului Œncauza. Expresia de indice nu se poate evalua la compilare (ea contine deregula identificatori), valoarea ei fiind obtinuta la executie.

Pentru fisiere, selectarea unei Œnregistrari se face dupa pozitia pe careŒnregistrarea respectiva o ocupa Œn fisier. Din nou, pozitia se poate da

36

Page 37: Sub Program Are

printr-o expresie care nu se poate evalua la compilare.

Domeniul de vizibilitate al numelor cƒmpurilor unei Œnregistrari Œncepe cupunctul lor de declarare si se termina la sfƒrsitul declaratiei tipuluiŒnregistrare. Selectarea unei componente (cƒmp) se face pe baza unei expresiide selectare, care contine numele cƒmpului calificat cu numele variabileiŒnregistrare. In acest caz, adresa relativa a cƒmpului Œn cauza se poatedetermina la compilare.

Pentru fiecare tip de date T introdus printr-o declaratie de tip, Turbo Pascalpune la dispozitia programatorului o functie numita SizeOf, care Œntoarcenumarul de octeti pe care se reprezinta o variabila de tipul respectiv.SizeOf poate se poate folosi - cu parametrul T: SizeOf(T); T este un nume de tip de date - cu parametrul V: SizeOf(V); V este o variabila de tipul T

Exemplu de folosire SizeOf: daca avem constructiile

[declararea lui T]Var V: T;

begin If SizeOf(V) = SizeOf(T) Then ... {expresia este True}End.

9.1. Tipul de date tablou

9.1.1. Conceptul de tablou9.1.2. Declararea tablourilor9.1.3. Accesarea tablourilor9.1.4. Tablouri bidimensionale9.1.5. Tablouri multidimensionale9.1.6. Operatii globale pe tablori9.1.7. Stringuri9.1.8. Tablouri deschise

Elementele definitorii ale unui tip de date tablou sunt:

- numele (optional),- lista dimensiunilor,- tipul elementului de tablou,- domeniul pentru multimea indicilor.

Numele unui tip tablou este un identificator. Exista si tipuri tablou anonime,care nu necesita declarare de tip, ele putƒndu-se folosi la declararea devariabile. Lista dimensiunilor precizeaza numarul de dimensiuni al tablouluirespectiv (monodimensional, bidimensional, tridimensional s.a.m.d), existƒndrestrictii de la limbaj la limbaj privind numarul maxim de dimensiuni permis.Tipul elementului de tablou defineste natura acestui element, precizƒndreprezentarea lui, iar domeniul pentru multimea indicilor este de obicei(dar nu Œntotdeauna) de tip subdomeniu, oferind informatie despre indicii

37

Page 38: Sub Program Are

valizi: pentru fiecare dimensiune se specifica limita inferioara 'li' silimita superioara 'ls', cu Ord(li) <= Ord(ls); numarul de elemente aldimensiunii respective este Ord(ls) - Ord(li) + 1. Numarul de elemente altabloului este produsul numerelor de elemente din fiecare dimensiune.Lungimea de reprezentare (dimensiunea alocata) al unei variabile de tiptablou este egal cu produsul dintre numarul de elemente al tabloului silungimea de reprezentare a elementului de tablou.

Criteriile de clasificare a tablourilor sunt cel putin urmatoarele:- numarul de dimensiuni;- tipul elementului (care poate induce operatii specifice);- momentul alocarii;- posibilitatea de redimensionare.

Din punctul de vedere al numarului de dimensiuni, Œntƒlnim tablourimonodimensionale (numite si vectori), tablouri bidimensionale (numite simatrici), tablouri tridimensionale, Œn general tablouri d-dimensionale.

La Œnceputurile folosirii calculatoarelor, cƒnd calculele stiintifice erauprincipalul domeniu de folosire a acestora, tablourile utilizate eraunumerice, Œn special vectori si matrici. In limbajele de programare actuale,tablourile pot avea o mare diversitate de tipuri de elemente: numerice,caractere, siruri de caractere, Œnregistrari, pointeri.

Din punctul de vedere al momentului alocarii, exista doua categorii detablouri: statice si dinamice. Tablourile statice se aloca la compilare (saucel putin trebuie cunoscute la compilare dimensiunile). Pentru tablouriledinamice, dimensiunile acestora se determina abia la executie. O categoriespeciala de tablouri dinamice o reprezinta tablourile flexibile, care se potredimensiona Œn timpul executiei.

Incepƒnd cu versiunea 7.0, Turbo Pascal permite si declararea tablourilordeschise, pentru care nu se precizeaza domeniul indicilor. De regula, acestetablouri apar ca parametri formali ai subprogramelor.

9.1.1. Conceptul de tablou

Tablourile se folosesc pentru a grupa variabile de tipuri identice si a lemanipula prin operatii. Daca fiecare variabila ar fi declarata individual,atunci fiecare operatie ar trebui specificata separat, fapt care ar duce laprograme lungi.

Un tablou ne permite sa grupam variabilele de acelasi tip sub un singur numesi sa putem referi fiecare variabila (numita element al tabloului) asociindnumelui un indice (care o va identifica unic). Prin aceasta se reduceconsiderabil dimensiunea unui program care efectueaza operatii similareasupra mai multor elemente din tablou, folosind indicii si instructiunile deciclare.

9.1.2. Declararea tablourilor

Incepƒnd cu Algol68 si Pascal, tipul tablou este integrat Œn sistemul de

38

Page 39: Sub Program Are

tipuri al limbajului. Intre altele, sistemul de tipuri verifica concordantatipurilor pentru parametrii formali si actuali de tip tablou la apelarea desubprograme, motiv pentru care este aproape impusa folosirea de tipuritablou cu nume. De altfel, limbajul Pascal nu permite specificarea unuiparametru formal de tip tablou ca tip anonim.

In Pascal, declaratia de tip tablou are sintaxa:

Type tip_tablou = Array[tip_index] Of tip_element;

unde:- Array si Of sunt cuvinte rezervate- tip_element, numit tipul componentei, poate fi orice tip recunoscut de sistemul de tipuri- tip_index este o lista de tipuri de indici; numarul elementelor din aceasta lista denota numarul de dimensiuni al tabloului, care nu este limitat. Tipurile indicilor trebuie sa fie ordinale (cu exceptia lui Longint si a subdomeniilor de Longint). Daca tipul componentei este tot un tip tablou, rezultatul acestei declaratii de tip poate fi tratat fie ca un tablou de tablouri, fie ca un tablou monodimensional. De exemplu, a doua declaratie:

type D = 20..30; type T = array[boolean] of array[1..10] of array[D] of real;

este identica (structural) cu declaratia:

type T1 = array[boolean, 1..10, D] of real;

iar referiri corecte de elemente sunt:

var A: T; A1: T1;

A[false] si A1[false] sunt de tip array[1..10] of array[D]of real; A[false,5] si A1[false,5] sunt de tip array[D] of real; A[false][5] si A1[false][5] sunt de tip array[D] of real; A[false,5,30] si A1[false,5,30] sunt de tip real; A[false][5][30] si A1[false][5]30] sunt de tip real;

Declararea de variabile de tip tablou nu trebuie neaparat sa contina numeleunui tip de date tablou. Constructorul de tip se poate include Œn declaratiade variabila. Astfel,

Var V: Array[1..10] Of Integer;

este o declaratie valida de variabila. In aceasta declaratie de variabila,numele tipului tablou este referit chiar prin constructorul de tip, adica

Array[1..10] Of Integer;

39

Page 40: Sub Program Are

In acest caz se spune ca folosim un tip de data anonim, adica fara nume(el n-are numele declarat Œntr-o declaratie de tip de date). Nu recomandam oasemenea practica, deoarece sistemul de tipuri al limbajului nu lucreaza asacum ne-am astepta noi cu tipurile anonime. Deci sa nu uitam o noua regula:

Nu folositi tipuri de date anonime.

Numarul de elemente al unui tablou este dat de produsul numarului de elementeŒn fiecare dimensiune.

9.1.3. Accesarea tablourilor

Accesarea (referirea) unui element de tablou se face prin precizareanumelui tabloului urmata de o expresie de indice. Expresia de indice contine,Œn paranteze drepte valorile efective ale indicilor tabloului (ce trebuie,de obicei, sa concorde ca numar si tip cu declararea acestuia). Exista Œngeneral doua modalitati de referire: punctuala si de subtablouri. Ambelemodalitati se bazeaza pe calculul de adresa.

Calculul de adresa realizeaza, pentru o expresie de indice data, determinarealocatiei de memorie (a adresei) ce contine elementul de tabloul referit.Deoarece memoria calculatorului poate fi considerata ca tablou monodimensionalde locatii adresabile, problema calculului de adresa se reduce la determinarea,pe baza informatiei asupra indicilor si asupra tabloului Œn general, a unuinumar ce reprezinta adresa cautata. Pentru fiecare tablou declarat, sememoreaza Œn descriptorul de tablou urmatoarele informatii:

- nume = numele tabloului;- tip = tipul elementului de tablou;- lung = lungimea reprezentarii unui element de tablou (Œn unitati de alocare);- adrs = adresa de unde Œncepe memorarea tabloului;- nrd = numarul de dimensiuni al tabloului;- pentru fiecare dimensiune i, limitele lii si lsi (1(i(nrd)

Se presupune ca tabloului i se aloca locatii consecutive de memorie, deci elocupa o zona compacta. Fiecare element de tablou va ocupa, Œn zona respectiva,o locatie unic determinata. Exista doua modalitati de memorare a unui tablou:pe linii (cƒnd ultimul indice variaza cel mai repede, engl. row major order),si pe coloane (cƒnd primul indice variaza cel mai repede, engl. column majororder). Notƒnd cu loc o functie ce Œntoarce adresa locatiei de memorie a uneireferinte de tablou, pentru doua tablouri A(li:ls) si B(li1:ls1,li2:ls2),calculele de adresa se descriu astfel:

loc(A(i)) = adrs + (i - li) * lung SAU loc(A(i)) = adrs - li * lung + i * lung

Memorare pe linii: toate elementele liniilor anterioare liniei i plus primele j-1 elemente ale liniei i;

loc(B(i,j)) = adrs + (i - li1) * (ls2 - li2 +1) * lung + (j - li2) * lung sau

40

Page 41: Sub Program Are

loc(B(i,j)) = adrs - li1 * (ls2 - li2 + 1) * lung - li2 * lung + j * lung + i * (ls2 - li2 + 1) * lung

Memorare pe coloane: toate elementele coloanelor anterioare coloanei j plus primele i-1 elemente ale coloanei j;

loc(B(i,j)) = adrs + (j - li2) * (ls1 - li1 +1) * lung + (i - li1) * lung sau

loc(B(i,j)) = adrs - li2 * (ls1 - li1 +1) * lung - li1 * lung + i * lung + j * (ls1 - li1 + 1) * lung

In expresiile de mai sus, ultimele relatii corespund unor calcule optime deadresa, prin gruparea la Œnceput a termenilor constanti (termenii ce nucontin indicii i si j ca factori).

Sintaxa expresiei de indice Œn Pascal permite folosirea indicilor de tipenumerare. Folosind tipul enumerare Luni, pe baza declaratiilor:

Type Luni = (Ian, Feb, Mar, Apr, Mai, Iun, Iul, Aug, Sep, Oct, Nov, Dec); Var temp : array[Luni] of real; {contine temperaturile lunare } i : luni; Const x: real = 0.0; { suma temperaturilor lunare }

instructiunea de ciclare:

For i := Ian To Dec Do x := x + temp[i];

care calculeaza Œn x suma temperaturilor lunare este valida. Sa remarcam aicica declaratia de variabila pentru temp foloseste un tip anonim.

9.1.4. Tablouri bidimensionale

Tablourile bidimensionale (numite si matrici Œn limbajul curent) au linii sicoloane. Am vazut Œn paragraful precedent cum se memoreaza aceste tablouri.Dam aici un exemplu care foloseste doua modalitati diferite (si structuralechivalente) de declarare a tablourilor bidimensionale.

Type DomeniuLinii = 1..25; DomeniuColoane = 1..80; Element = Record Car: Char; Atr: Byte End; TabEcran1 = Array[DomeniuLinii, DomeniuColoane] Of Element; TabEcran2 = Array[DomeniuLinii] Of Array[DomeniuColoane] Of

41

Page 42: Sub Program Are

Element;

Var EcranColor1: TabEcran1 Absolute $B800:0000; EcranMono1: TabEcran1 Absolute $B000:0000; EcranColor2: TabEcran2 Absolute $B800:0000; EcranMono2: TabEcran2 Absolute $B000:0000;

Begin EcranColor1[12, 33].Car := 'A'; Write(EcranColor2[12][33].Car); EcranMono2[11][25].Car := 'B'; Write(EcranMono1[11,25].Car);End.

Prima modalitate de declarare permite accesarea - punctuala: EcranColor1[12, 33] - globala: EcranColor1

A doua modalitate de declarare a tabloului permite accesarea - punctuala: EcranColor2[12, 33] sau EcranColor2[12][33] - a unei linii: EcranColor2[12] (care va fi de tipul Array[DomeniuColoane] Of Element) - globala: EcranColor2

9.1.5. Tablouri multidimensionale

Au mai mult de doua dimensiuni. Limbajul Pascal nu impune o regula privindnumarul maxim de dimensiuni al unui tablou. Trebuie avuta Œn vedere doarrestrictia ca spatiul alocat unei variabile sa nu depaseasca dimensiuneasegmentului de date sau de stiva.

9.1.6. Operatii globale pe tablori

Operatiile definite pe tipul tablou sunt atribuirea si testul de egalitate.De asemenea, variabilele de tip tablou se pot transmite ca parametri Œnsubprograme.

9.1.7. Stringuri

Amintim ca Œn Standard Pascal apare (Œntre primele) tipul sir de caractereŒmpachetat care este declarat prin:

packed array[li..lf] of Char; li ó lf

Pentru compatibilitate cu Standard Pascal, Turbo Pascal accepta si astfel dedeclaratii, Œnsa packed nu are nici un efect. La vremea respectiva (cƒnd aaparut standardul Pascal), setul de caractere folosit era setul ASCII (uncaracter reprezentat pe 7 biti).

Un String de lungime N caractere (N > 1) este considerat Œn Pascal standardca fiind constanta de tipul:

42

Page 43: Sub Program Are

packed array[1..N] of Char;

Variabilele de tipul packed array[1..N] of Char se numesc variabile stringde lungime N.

In Turbo Pascal este definit tipul String, cu lungime dinamica, Œn doua forme:

Type String = Array[0..255] Of Char; String[N] = Array[0..N] Of Char;

Operatiile si subprogramele de lucru cu variabile String au fost discutateŒn cap. 5 (Expresii).

9.1.8. Tablouri deschise

In Turbo (Borland) Pascal 7.0 se pot folosi si tablouri deschise (engl. openarrays) pe post de parametri formali Œn declaratiile de subprograme.Declararea unui tablou deschis nu se poate face cu sintaxa:

Type tip_tablou = Array Of tip-element; { declaratie incorecta }

ci doar ca tip anonim, Œntre parametrii unei proceduri sau functii. Pentrutablourile deschise, Turbo Pascal are doua functii standard, numite Low siHigh (parametrul lor este numele tabloului deschis) care Œntorc limitainferioara, respectiv limita superioara a tabloului deschis.

Functia Low(A) Œntoarce cea mai mica valoare a domeniului argumentului A.In cazul general, A poate sa fie identificator de tip sau nume de variabilade tip ordinal, tablou, sir de caractere si tablou deschis. Valorile Œntoarsesunt precizate Œn tabelul urmator:

----------------------------------------------------------------------- Tipul lui A Ce Œntoarce Low(A) ----------------------------------------------------------------------- Ordinal cea mai mica valoare a domeniului tipului Array cea mai mica valoare a domeniului indicilor tabloului String 0 Open array 0 parametru String 0 -----------------------------------------------------------------------

Functia High(A) Œntoarce cea mai mare valoare a domeniului argumentului A,care are semantica de la Low. Valorile Œntoarse sunt precizate Œn tabelulurmator:

------------------------------------------------------------------------ Tipul lui A Ce Œntoarce High(A) ------------------------------------------------------------------------ Ordinal cea mai mare valoare a domeniului tipului

43

Page 44: Sub Program Are

Array cea mai mare valoare a domeniului indicilor tabloului String dimensiunea declarata a sirului de caractere Open array numarul de elemente din tablou - 1 parametru String numarul de caractere din sir - 1 ------------------------------------------------------------------------

Exemplul urmator prezinta folosirea tabloului deschis la determinareaelementului minim dintr-un tablou de numere. Functia Min este generala, eaacceptƒnd orice tablou de numere reale ca parametru, tocmai prin folosireatabloului deschis. In corpul ei, determinarea primului si ultimului indicese face cu ajutorul functiilor Low si High descrise anterior.

Program TabDesc;

Function Min(var A: array of Real): Real; {A este parametru tablou deschis } Var I: integer; M: real; begin M := 10e20; for I := Low(A) to High(A) do if A[I] < M then M := A[I]; Min := M end;

const { constante tablou cu tip }

CA1: array[5..20] of real =(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); CA2: array[-5..7] of real =(-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7);

Begin WriteLn(Min(CA1):10:2); WriteLn(Min(CA2):10:2); WriteLn End. { TabDesc }

Pentru un parametru tablou cu declaratia

T : Array[li..lf] of Real,

care este argument de apel al functiei Min din exemplul anterior, la executiaacesteia parametrul formal A va fi considerat de tipul:

A : array[0..nr-1] of Real;

unde nr este numarul de elemente al tabloului parametru actual, adicalf - li + 1. Prin urmare, are loc o translatie a domeniului indicilor, de lali..lf la 0..nr-1, limitele efective (de lucru) ale indicelui fiind Œntoarsede functiile Low si High: A[0] (adica A[Low(A)]) corespunde lui T[li], A[1]lui T[li+1], ... A[nr-1] (adica A[High(A)]) corespunde lui T[lf].

44

Page 45: Sub Program Are

9.2. Tipul de date multime

9.2.1. Definitia tipurilor multime9.2.2. Construirea multimilor si atribuirea9.2.3. Compararea multimilor si testul de apartenenta9.2.4. Operatii cu multimi

9.2.1. Definitia tipurilor multime

Limbajul Pascal este unul din primele limbaje care a introdus tipul multime.Fiind dat un tip ordinal B, numit tip de baza, tipul multime T se declarafolosind sintaxa:

Type tip_multime = Set Of tip_de_baza;

Domeniul tipului tip_multime T este multimea partilor (submultimilor)domeniului tipului tip_de_baza. Astfel, daca tipul B are domeniul {a, b, c},atunci domeniul lui T va fi

{{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}}.

Tipul T se considera structurat deoarece fiecare element al sau contineelemente (posibil nici unul) din domeniul tipului de baza B.

Implementarea tipului multime se face pe siruri de biti. Daca domeniul tipuluide baza B are n elemente, atunci domeniul tipului multime T va avea 2^n(2 la puterea n) elemente, deci o variabila de tip T se poate reprezinta peun sir de n biti. Bitul de pe pozitia i din sirul respectiv (1óión) va fisetat pe 1 daca al i-lea element din domeniul lui B (ordinal) apartinemultimii si 0 Œn caz contrar. Operatiile tipului multime se implementeazaeficient prin operatii pe siruri de biti. In cazul limbajului Pascal, n = 256.

Multimile reprezinta un instrument matematic elegant. Prezenta lor Œnlimbajele de programare Œmbogateste expresivitatea acestuia. Din pacate,limitarile impuse asupra domeniului tipului de baza (numar de elemente si tip)restrƒng utilizarea multimilor. In astfel de situatii, utilizatorul poatesa-si defineasca propriile sale tipuri multime.

9.2.2. Construirea multimilor si atribuirea

Inainte de a opera cu variabile de tip multime, ele trebuie construite.Regulile de construire a variabilelor de tip multime Œn Pascal sunturmatoarele:- o multime Pascal este formata dintr-o lista de elemente separate prin virgula si incluse Œn paranteze drepte;- multimea vida se marcheaza prin [];- toate elementele din lista trebuie sa fie de acelasi tip, tipul de baza al multimii- toate elementele sunt diferite- Œn loc sa se enumere toate elementele dintr-un subdomeniu, se poate preciza subdomeniul pe post de element Œn lista (primul si ultimul element cu ..

45

Page 46: Sub Program Are

Œntre ele)

Ordinea Œn care se executa operatiile:- declararea tipului multime T- declararea variabilei V de tipul T- construirea (initializarea) variabilei V

Exemple:

Type Luni = (Ian, Feb, Mar, Apr, Mai, Iun, Iul, Aug, Sep, Oct, Nov, Dec); Anotimp = Set Of Luni; Caractere = Set Of Char; Var Iarna, Primavara, Vara, Toamna: Anotimp; Litere, Cifre: Caractere;

Begin Iarna := [Dec, Ian, Feb, Mar]; Vara := []; Toamna := [Aug, Sep, Oct, Nov]; Primavara := [Apr, Mai, Iun, Iul]; If Vara = [] Then WriteLn('Anul asta n-a fost vara!'); Litere := ['A'..'Z', 'a'..'z']; Cifre := ['0'..'9'] End.

9.2.3. Compararea multimilor si testul de apartenenta

Asa cum am discutat Œn cap. 5, Pascal are cinci operatori relationali careopereaza cu expresii de tipuri multime ca operanzi si care sunt prezentati Œntabelul urmator. Primii patru dintre ei cer ca ambii operanzi sa fie tipurimultime compatibile, iar ultimul are un operand de tipul B si altul de tipulT unde T este Set Of B.

----------------------------------------------------------------------------Operator Descriere Exemplu de folosire Rezultat----------------------------------------------------------------------------= egalitate de multimi [Ian, Feb] = [ ] False<> diferit [Ian, Mar] <> [Apr] True<= incluziunea A <= B [Ian, Mar] <= [Ian, Aug, Mar] True inseamna ca orice element [Ian, Mar] <= [Feb] False din A apartine si lui B>= include: A >= B Œnseamna [Ian, Aug, Mar] >= [Ian, Apr] False ca orice element din B [Ian, Mar] >= [Mar] True apartine si lui Ain x in A Œnseamna x Ian in [Aug, Feb] False apartine lui A Mar in [Ian, Aug, Mar] True x trebuie sa fie din 'P' in ['A'..'Z'] True tipul de baza----------------------------------------------------------------------------

46

Page 47: Sub Program Are

9.2.4. Operatii cu multimi

Operatiile proprii tipului multime sunt cele cunoscute: reuniunea,intersectia, diferenta (operatii binare cu rezultat multimi), incluziunea,testul de egalitate (operatii binare cu rezultat boolean), apartenenta,atribuirea. Ele au fost discutate Œn capitolul 5 (Expresii).

Incepƒnd cu versiunea 7.0, Turbo Pascal poseda doua noi proceduri, Includesi Exclude. Procedura Include, declarata prin

procedure Include(var S: set of T; I:T);

are ca efect adaugarea elementului I la multimea S, iar procedura Exclude, cuaceeasi signatura, are ca efect eliminarea elementului I din multimea S.

9.3. Tipul de date Œnregistrare9.3.1. Conceptul de Œnregistrare9.3.2. Declararea tipului Œnregistrare9.3.3. Accesarea componentelor. Instructiunea With9.3.4. Structuri complexe9.3.5. Inregistrari cu variante9.3.6. Initializarea Œnregistrarilor

9.3.1. Conceptul de Œnregistrare

Elementele definitorii ale unei Œnregistrari sunt:

- numele tipului Œnregistrare (optional),- numarul de cƒmpuri,- numele si tipul fiecarui cƒmp.

Inregistrarea este o modalitate de agregare (punere Œmpreuna a unor date detipuri (Œn general) diferite. Numele tipului de data Œnregistrare este unidentificator. Numarul de cƒmpuri este dedus din lista de declarare acƒmpurilor. Cƒmpurile unei Œnregistrari se memoreaza Œn zone adiacente dememorie. Informatia de tip a fiecarui cƒmp serveste la stabilirea lungimii dereprezentare a acestuia, iar numele cƒmpului se foloseste pentru a accesavaloarea lui (prin operatia de accesare). De obicei, sunt accesibile atƒtdata compusa (Œnregistrarea), cƒt si componentele (cƒmpurile) acesteia.

Exista doua clase de Œnregistrari: cu structura fixa si cu variante.Inregistrarile cu structura fixa au o singura definitie, iar Œnregistrarilecu variante au o parte fixa, un discriminant si mai multe definitii devariante.

Lungimea unei Œnregistrari este suma lungimilor cƒmpurilor componente. Sepoate folosi functia SizeOf cu parametru tipul de data Œnregistrare sauvariabila de tip Œnregistrare.

Elementele de discutie privitoare la tipul de date Œnregistrare sunturmatoarele:- maniera de declarare a tipurilor Œnregistrare;

47

Page 48: Sub Program Are

- maniera de accesare a cƒmpurilor unei Œnregistrari;- definirea Œnregistrarilor cu structura variabila;- initializarea unei variabile Œnregistrare;- operatiile permise pe variabile de tip Œnregistrare.

9.3.2. Declararea tipului Œnregistrare

Odata cu Pascal si Algol68, prin mecanismele de tipizare se pot declaratipuri de date Œnregistrare. In Pascal, de exemplu, tipurile de dateDataC (data calendaristica), Timp si ElementEcran se declara astfel:

Type DataC = Record zi : 1..31; lu : 1..12; an : 1800..2000; End; Timp = Record ora: 0..23; min: 0..59; sec: 0..59 End; Element = Record { definitia unui element pe ecranul text } Car: Char; Atr: Byte End;

Se observa ca aceste tipuri de date au componente (cƒmpuri) diferite ca tipde date. Odata declarate aceste tipuri, se pot declara variabile de tipurilerespective:

Var DataNasterii, DataCasatoriei: DataC; OraDeIncepere: Timp; Ecran: Array[1..25] Of Array[1..80] Of ElementEcran;

Doar Œn momentul declaratiei de variabila se poate stabili dimensiunea dealocare pentru variabilele de tipurile respective.

9.3.3. Accesarea componentelor. Instructiunea With

Referirea componentelor sau selectarea (accesarea) cƒmpurilor uneiŒnregistrari se face Œn Pascal Œn doua moduri- folosind calificarea cu punct- folosind instructiunea With

Calificarea cu punct permite accesarea sau modificarea valorii unui cƒmpdintr-o variabila Œnregistrare. Ea se face Œn forma:

nume_variabila.nume_cƒmp

In Pascal, daca se acceseaza succesiv mai multe cƒmpuri ale aceleiasi

48

Page 49: Sub Program Are

variabile de tip Œnregistrare, se poate folosi instructiunea With (vezi 9.3.3si 7.7).

Variabilele de tip Œnregistrare se pot accesa si global, prin numele lor.Este permisa de asemenea operatia de atribuire, cƒnd variabilele implicatesunt de acelasi tip:

Var Data1, Data2: DataC;

----------------------------------------------Atribuire globala Echivalenta cu----------------------------------------------Data1 := Data2 Data1.zi := Data2.zi Data1.lu := Data2.lu Data1.an := Data2.an----------------------------------------------

9.3.4. Structuri complexe

Cƒmpurile unei Œnregistrari pot fi de orice tip recunoscut de sistemul detipuri, Œn particular si tip Œnregistrare.

Type DataC = Record zi : 1..31; lu : 1..12; an : 1800..2000; End; A40 = Array[1..40] Of Char; TipSex = (masc,fem); StareCiv = (necas,casat,vaduv,divortat); Persoana = Record Nume : String[30]; NrId : Longint; Adr : A40; Sex : TipSex; StareC : StareCiv; DataN : Datac; inalt : Real End;

Tipurile cƒmpurilor sunt predefinite (String, LongInt, Real Œn Turbo Pascal),simple utilizator (TipSex si StareCiv), respectiv structurate (A40 si DataC).Prin urmare, cu astfel de notatii de tip se pot declara tipuri oricƒt decomplexe. Daca se declara o variabila de tip Persoana:

Var p : Persoana;

ea va putea fi tratata fie global (atribuire, test de egalitate), fie selectiv(p.Inalt va semnifica cƒmpul inalt din Œnregistrarea p). Tabelul urmator

49

Page 50: Sub Program Are

prezinta exemple de accesare (cu punct si cu With).

With P Do BeginP.Nume := 'IONESCU'; Nume := 'IONESCU';P.Sex := masc; Sex := masc;P.StareC := casat; StareC := casat;P.Inalt := 1.80; Inalt := 1.80;P.DataN.zi := 23; DataN.zi := 23 End

Alte exemple de structuri complexe- Œnregistrari cu cƒmpuri tablou- tablouri de Œnregistrari

Const Max_Persoane = 100; Max_Discipline = 20;Type Personal: Array[1..Max_Persoane] Of Persoana; Medie = Record {facem economie: Medie: Real ar ocupa 6 byte } Parte_Int: Byte; {SizeOf(Medie) = 2} Parte_Zec: Byte End; { Medie } Medii = Array[1..Max_Discipline] Of Medie; Trimestru = 1..3; Elev = Record Nume: String[40]; DataN: DataC; {declarat Œn exemplul de mai sus} SitScolara: Array[Trimestru] Of Medii; End; { Elev }

9.3.5. Inregistrari cu variante

Inregistrarile date ca exemplu pƒna acum au o structura fixa. O Œnregistrarecu structura variabila contine diferite definitii alternative ale unoradintre componentele sale. De obicei, o Œnregistrare cu structura variabilaare o parte fixa, comuna tuturor alternativelor sale, si o parte variabila,a carei structura trebuie definita explicit Œn fiecare alternativa. Parteavariabila este identificata prin valoarea pe care o poate lua un cƒmp specialal Œnregistrarii numit 'discriminant'. Valori distincte ale discriminantuluivor produce alternative distincte. Modul Œn care se precizeaza alternativelepartii variabile este de obicei de tip case. In Pascal, sintaxa partiivariabile este:

<parte variabila record> ::= case <camp discriminant> <identificator de tip> of <varianta> <camp discriminant> ::= [<identificator>:] <varianta> ::= [<lista etichete case>:(<lista campuri>)]

cu precizarea general valabila ca partea fixa a Œnregistrarii trebuiedeclarata Œnaintea partii variabile.

50

Page 51: Sub Program Are

Inregistrarile cu variante respecta principiul de reprezentare a celor custructura fixa. Lungimea de reprezentare a unei variabile de tip Œnregistrarecu variante este de obicei lungimea partii fixe plus maximul lungimiivariantelor. Partea fixa contine inclusiv cƒmpul discriminant. Prin urmare,Œntr-o Œnregistrare cu variante, primele cƒmpuri ale variantelor au aceeasiadresa relativa la Œnceputul Œnregistrarii.

De exemplu, Œn Pascal se poate defini o Œnregistrare cu variante astfel:

Type CuloareOchi = (caprui,albastru,negru,verde); Sex = (Masc,Fem); Persoana = record Nume : string[30]; NrId : longint; Adr : array[1..40] of char; Case s:Sex of { discriminant } Masc : (Inalt: Real; DataN: DataC); Fem : (CO: CuloareOchi; Bust:Real) End;

Partea fixa a Œnregistrarii Persoana contine cƒmpurile Nume, NrId, Adr si S,iar pentru partea variabila exista doua alternative. Daca valoareadiscriminantului S este Masc, atunci vor putea fi accesate cƒmpurile Inalt siDataN, iar daca S are valoarea Fem cƒmpurile valide sunt CO si Bust.

Un alt exemplu:

Type ambele = (by,ch); bc = record case t:ambele of by : (b:byte); ch : (c:char); end; var a1, a2, a3 : bc;

Prin urmare variabilele a1, a2, a3 vor contine fie cƒmpuri de tip byte, fiecƒmpuri de tip caracter. Sunt permise urmatoarele instructiuni:

a1.t := ch; {discriminantul ch: accesibil a1.c } a1.c := 'a'; a1.t := by; {discriminantul by: accesibil a1.b } writeln(a1.c); { care nu este initializat }

Accesarea variantei a1.c cu valoarea discriminantului a1.t=by reprezinta outilizare inconsistenta a variabilei a1. Acest lucru nu ar trebui sa fiepermis, deoarece cƒmpul discriminant Œsi pierde astfel "puterea de decizie"Œn ceea ce priveste varianta aleasa, rolul sau devenind practic Œn acest cazunul pur decorativ.

Folosirea sistemului de tipuri pentru a realiza verificarile de consistenta

51

Page 52: Sub Program Are

necesare nu este posibila Œn cazul tipizarii statice, specifica limbajuluiPascal.

9.3.6. Initializarea Œnregistrarilor

Inregistrarile se pot initializa folosind constante cu tip. Declararea uneiconstante de tip Œnregistrare specifica numele (identificatorul) si valoareaasociata pentru fiecare cƒmp. Cƒmpurile trebuie specificate Œn ordineadeclararii lor Œn tipul Œnregistrare..

Nu se pot initializa Œnregistrari ale caror tipuri contin cƒmpuri de tip file.

type Punct = record X, Y: Real; end; Vector = array[0..1] of Punct; Luna = (Ian,Feb,Mar,Apr,Mai,Iun,Iul,Aug,Sep,Oct,Nov,Dec); DataC = Record zi : 1..31; lu : Luna; an : 1800..2000; End;const Origine: Punct = (X: 0.0; Y: 0.0); Linie: Vector = ((X: -3.1; Y: 1.5), (X: 5.8; Y: 3.0)); ZiuaMea: DataC = (zi: 2; lu: Dec; an: 1960);

9.4. Tipul de date fisier

9.4.1. Notiunea de fisier9.4.2. Operatii asupra fisierelor9.4.3. Fisiere text9.4.4. Fisiere record9.4.5. Fisiere fara tip (stream)9.4.6. Fisierele sistem

9.4.1. Notiunea de fisier

Programele realizate pƒna acum au folosit date de intrare furnizate de latastatura si au prezentat rezultatele pe ecran. In cazul mai general, unprogram P va prelua date dintr-un fisier de intrare si va furniza rezultateŒntr-un fisier de iesire. Spunem Œn acest caz ca P efectueaza operatii deintrare/iesire (I/E).

Implicit, un program Pascal considera ca preia datele de intrare de lafisierul standard de intrare al sistemului de operare si ca scrie rezultateleŒn fisierul standard de iesire al sistemului. Uzual, fisierul standard deintrare este tastatura sau un dispozitiv special de introducere de date, pecƒnd fisierul standard de iesire este ecranul terminalului sau imprimanta. In9.4.6. vom discuta despre aceste fisiere sistem.

52

Page 53: Sub Program Are

Fisierul este o colectie de informatii. Aceste informatii se pot structura ŒnPascal sub forma de text, sub forma de date atomice (de tipuri simple,predefinite) sau sub forma de Œnregistrari (record). Turbo Pascal poseda sifisiere cu structura nedefinita, numite fisiere fara tip. Fisierul se poateconsidera din doua puncte de vedere- fizic sau extern programului (respectƒnd conventiile sistemului de operare): specificator de fisier- logic sau intern programului care-l foloseste: identificator logic de fisier.

Din punct de vedere fizic, un fisier este identificat de- perifericul pe care este memorat- directorul (calea) Œn care se afla- numele sau (nume.tip)iar din punct de vedere logic, un fisier este manipulat Œntr-un programprintr-o variabila de tip fisier. In Pascal tipul fisierului se construiestespecific.

9.4.2. Operatii asupra fisierelor

Fisierele sunt folosite pentru a pastra (memora) informatie pe suport extern.Aceste informatii au un caracter persistent, adica ele exista si dupa ceprogramul care le-a creat Œsi termina executia. Spre exemplu, programelesursa Borland (Turbo) Pascal se gasesc Œn fisiere text, care sunt create cuajutorul unui editor de texte. Odata introdus textul sursa, acesta poate fimodificat dupa dorinta programatorului, folosind un editor similar.

Operatiile efectuate asupra fisierelor Œntr-un program Pascal sunt:- declararea- deschiderea- Œnchiderea- pozitionarea- citirea- scrierea

Ordinea Œn care aceste operatii se efectueaza este de obicei urmatoarea:- declararea fisierului- deschiderea fisierului, care Œnseamna si o pozitionare implicita- citirea sau scrierea de informatie din/Œn fisier, precedate sau nu de operatii de pozitionare- Œnchiderea fisierului

Fisierele servesc la transferul de informatie dintre memoria interna sisuportul extern (periferice). Operatiile de transfer sunt citirea (transferulde informatie din fisier Œn memoria interna) si scrierea (transferul deinformatie din memoria interna Œn fisier).

Operatiile de pozitionare sunt necesare pentru a preciza locul de unde seciteste informatia (Œn cazul citirii), respectiv locul de unde Œncepescrierea de informatie pe fisier (in cazul scrierii). Fiecare fisier non-textdeschis are asociate o zona de memorie, numita buffer, care serveste caintermediar Œntre memoria interna si fisier, continƒnd informatia supusatransferului. Toate fisierele au asociat un Œntreg numit contor de pozitie,

53

Page 54: Sub Program Are

care specifica pozitia curenta din fisier de la care Œncepe transferul.

Exceptƒnd declararea, necesara datorita caracterului puternic tipizat allimbajului, toate celelalte operatii enumerate se traduc Œn operatii fizicede accesare a suportului pe care se gaseste fisierul, fiind numite genericoperatii de intrare-iesire. Aceste operatii pot sa provoace mari batai de capprogramatorilor, motiv pentru care mediile Turbo si Borland Pascal le pun ladispozitie o modalitate de verificare a modului de terminare a lor, cuajutorul functiei standard IOResult, care Œntoarce starea ultimei operatiide intrare-iesire efectuate.

Declaratia acestei functii este urmatoarea:

function IOResult: Integer;

si ea Œntoarce:- 0 daca ultima operatie de intrare/iesire s-a terminat cu succes- o valoare diferita de 0 (de obicei un cod al erorii produse) daca ultima operatie de intrare-iesire a esuat.

Pentru ca programatorul sa poata folosi aceasta functie, trebuie sa se comuteindicatorul $I de verificare a operatiilor de intrare-iesire pe off. Acestlucru se poate face fie incluzƒnd Œn cod directiva de compilare {$I-}, fiefolosind meniul Options|Compiler|I/O Checking. Uzual, comutatorul $I estesetat pe on, adica {$I-}.

9.4.2.1. Declararea fisierelor

La nivelul programului sursa Pascal, un fisier este referit printr-o variabilafisier, ceea ce am numit anterior identificator logic de fisier. DeoarecePascal este un limbaj puternic tipizat, identificatorul logic de fisiertrebuie sa fie de una dintre urmatoarele tipuri:- text (pentru fisiere text)- file of tip_componenta (fisierul este un fisier record Œn care fiecare Œnregistrare este de tipul tip_componenta)* file (fisierul este nedefinit, fiind considerat ca o succesiune de octeti - stream)

Declararea variabilelor fisier se face uzual, folosind cuvƒntul rezervat var.

Variabilele fisier nu se pot folosi decƒt Œn operatiile specifice lucrului cufisiere. Mai exact, o variabila fisier se initializeaza prin operatia dedeschidere a fisierului si Œsi pierde valoarea la Œnchiderea acestuia.

varfis_text: Text; { fisier text }fis_intregi = File of Integer; { fisier de intregi }fis_nedefinit: File; { fisier nedefinit }

9.4.2.2. Deschiderea unui fisier

Operatia de deschidere a unui fisier este prima operatie care se efectueaza

54

Page 55: Sub Program Are

asupra acestuia, Œnaintea prelucrarilor propriu-zise la care acesta estesupus. Menirea acestei operatii este de a stabili- modul Œn care este folosit fisierul (citire sau scriere)- alocarea buffer-ului si initializarea contorului de pozitie.

In Turbo si Borland Pascal, operatia de deschidere a unui fisier serealizeaza Œn doi pasi:- initializarea variabilei fisier;- deschiderea propriu-zisa a fisierului.

Initializarea variabilei fisier se face cu procedura standard Assign, careare declaratia:

procedure Assign(var var_fis; nume_fis:String);

unde:- var_fis este o variabila fisier de oricare tip (identificatorul logic de fisier), iar- nume_fis este un sir de caractere ce desemneaza numele extern al fisierului (specificatorul de fisier)

Dupa executia procedurii, var_fis va fi initializata, fiind asociatafisierului extern nume_fis; cu alte cuvinte, orice operatie pe var_fis vaŒnsemna de fapt lucrul cu fisierul nume_fis. Asocierea ramƒne valabila pƒnacƒnd se Œnchide fisierul referit de var_fis sau pƒna cƒnd var_fis apare Œntr-oalta procedura Assign.

Daca nume_fis este sirul de caractere vid, var_fis va fi asociata unuia dintrefisierele sistem. Daca nume_fis este numele unui fisier deja deschis, seproduce o eroare de executie.

Deschiderea propriu-zisa a unui fisier se face Œn mod obisnuit prin apeluluneia dintre procedurile standard Reset sau Rewrite.

Procedura standard Reset realizeaza deschiderea unui fisier (de obicei) Œncitire. Declaratia sa este:

procedure Reset(var var_fis);

unde var_fis este o variabila fisier de oricare tip (identificatorul logic defisier), asociat Œn prealabil unui fisier extern prin apelul proceduriiAssign.

Reset deschide fisierul extern asociat variabilei var_fis. Daca fisierulrespectiv este deja deschis, el se Œnchide Œn prealabil si apoi are locdeschiderea. Contorul de pozitie al fisierului se seteaza pe Œnceputulfisierului. Reset produce o eroare de intrare-iesire daca fisierul extern nuexista. In cazul fisierelor text, Reset produce deschiderea acestora numai Œncitire.

Pentru fisierele non-text, Unit-ul System contine variabila FileMode, carecontine informatia privitoare la modul de deschidere a acestora prin Reset:

55

Page 56: Sub Program Are

- 0 - Read only (numai citire)- 1 - Write only (numai scriere)- 2 - Read/Write (valoare implicita).

Aceasta variabila se poate seta de catre programator prin operatia deatribuire.

Functia Exista din programul CitireT (9.4.2.3) verifica daca fisierulspecificat prin parametrul NumeFisier (care reprezinta un specificatorcomplet de fisier) exista pe perifericul specificat Œn el. Daca perifericulsi calea lipsesc, atunci se considera ca fisierul este Œn directorul curent.

Procedura standard Rewrite realizeaza deschiderea unui fisier Œn scriere.Declaratia sa este:

procedure Rewrite(var var_fis);

unde var_fis este o variabila fisier de oricare tip (identificatorul logic defisier), asociat Œn prealabil unui fisier extern prin apelul proceduriiAssign.

Rewrite creeaza fisierul extern asociat variabilei var_fis. Daca fisierulrespectiv exista, atunci el se sterge si se creeaza un fisier vid. Daca eleste deja deschis, atunci se Œnchide Œn prealabil si apoi este re-creat.Contorul de pozitie al fisierului se seteaza pe Œnceputul fisierului.In cazul fisierelor text, Rewrite produce deschiderea acestora numai Œnscriere.

9.4.2.3. Inchiderea unui fisier

Operatia de Œnchidere a unui fisier semnifica terminarea lucrului cu acesta.Prin Œnchidere se realizeaza urmatoarele:- transferarea informatiei din buffer pe suport (Œn cazul fisierelor deschise Œn scriere)- distrugerea asocierii Œntre variabila fisier si numele extern al fisierului.

Dupa Œnchidere, fisierul se poate folosi din nou, respectƒnd etapele descriseanterior. Variabila fisier devine disponibila, ea putƒnd fi folosita Œntr-unalt apel al procedurii Assign.

Pentru un fisier deschis Œn citire, prelucrarea sa se termina de regulaatunci cƒnd s-a ajuns la sfƒrsitul sau. Detectarea sfƒrsitului de fisier seface cu functia standard EOF. Declaratia acesteia este:

function Eof(var var_fis): Boolean; { fisiere non-text } function Eof [ (var var_fis: Text) ]: Boolean; { fisiere text }

In cazul fisierelor text, daca var_fis lipseste se considera ca EOF se referala fisierul standard de intrare. Altfel, var_fis va referi un fisier deschisŒn prealabil prin Assign si Reset/Rewrite. EOF Œntoarce:- True daca contorul de pozitie este dupa ultimul octet din fisier sau daca fisierul este vid

56

Page 57: Sub Program Are

- False Œn toate celelalte cazuri.

Exemplu: citirea unui fisier text si afisarea acestuia pe ecran;

Program CitireT; var f: text; linie: string; numeFis: String; este: Boolean;

function Exista(NumeFisier: String): Boolean; { intoarce True daca fisierul extern reprezentat de NumeFisier exista False altfel } var f: File; begin {$I-} { comuta pe off indicatorul $I } Assign(f, NumeFisier); FileMode := 0; { deschide in citire } Reset(f); Close(f); {$I+} Exista := (IOResult = 0) and (NumeFisier <> '') end; { Exista }

begin WriteLn('CitireT - afisarea unui fisier text pe ecran'); Repeat Write('Dati numele fisierului: '); ReadLn(numeFis); este := Exista(numeFis); if not este then WriteLn('Fisier inexistent!'); Until este; Assign(f, numeFis); { asociaza f la numeFis } Reset(f); { deschide f in citire } While not Eof(f) do begin { cat timp nu s-a ajuns la sfarsit } Readln(f, linie); { citeste o linie din fisier } Writeln(linie) { scrie linia la iesirea standard } end; Close(f); { inchide fisierul } end. { CitireT }

9.4.2.4. Pozitionarea

Operatia de pozitionare se refera la fisierele non-text si are ca efectmodificarea contorului de pozitie. Modificarea se face Œn doua moduri:- implicit, prin operatiile de citire si de scriere; orice operatie de transfer modifica contorul de pozitie, acesta avansƒnd spre capatul fisierului- explicit, prin procedura standard Seek.

57

Page 58: Sub Program Are

Mediile Borland pun la dispozitia programatorului o functie si o proceduracare Œi permit acestuia sa acceseze si sa modifice contorul de pozitie:FilePos si Seek. De asemenea, functia FileSize determina dimensiunea unuifisier.

Functia FileSize Œntoarce numarul de componente dintr-un fisier. Declaratiasa este urmatoarea:

function FileSize(var var_fis): Longint;

Œn care var_fis este o variabila fisier, asociata unui fisier deschis Œnprealabil. FileSize Œntoarce numarul de componente al fisierului. Dacafisierul este vid, FileSize Œntoarce 0. Vom discuta Œn paragrafele urmatoaresemantica exacta a lui FileSize pentru fiecare clasa de fisiere.

Functia FilePos Œntoarce valoarea contorului de pozitie al unui fisiernon-text. Declaratia sa este:

function FilePos(var var_fis): Longint;

Œn care var_fis este o variabila fisier, asociata unui fisier deschis Œnprealabil. Daca contorul de pozitie este pe Œnceputul fisierului, FilePos vaŒntoarce 0, iar daca contorul de pozitie este la sfƒrsitul fisierului, atunciFilePos(var_fis) va fi egal cu FileSize(var_fis).

Contorul de pozitie al unui fisier non-text se exprima Œn unitati de masuraproprii tipului de fisier. El semnifica Œnregistrarea curenta din fisier,luƒnd valori de la 0 (Œnceputul fisierului, prima Œnregistrare din el) laFileSize(var_fis) - 1 (ultima Œnregistrare din fisier). Procedura standardSeek realizeaza modificarea contorului de pozitie la o noua valoare,specificata ca parametru al acesteia. Declaratia sa este:

procedure Seek(var var_fis; pozitie: Longint);

Œn care:- var_fis este o variabila fisier, asociata unui fisier deschis Œn prealabil- pozitie este un Œntreg, cu urmatoarele valori valide: - Œntre 0 si FileSize(var_fis) - 1: contorul de pozitie al fisierului var_fis se va seta la valoarea pozitie - FileSize(var_fis): Œn fisierul var_fis se va adauga (la sfƒrsit) o noua Œnregistrare

Exista, de asemenea, procedura Truncate, care permite trunchierea unui fisiernon-text. Declaratia sa este:

procedure Truncate(var var_fis);

Œn care var_fis este o variabila fisier, asociata unui fisier deschis Œnprealabil. Truncate pastreaza Œn fisier numai Œnregistrarile de la 0 si pƒnala FilePos(var_fis) - 1, eliminƒnd celelalte Œnregistrari din el (daca exista)si setƒnd EOF(var_fis) pe True.

58

Page 59: Sub Program Are

9.4.2.5. Citirea

Operatia de citire Œnseamna transferarea de informatie din fisierul extern Œnmemoria interna a calculatorului. Mai exact, citirea realizeaza initializareaunor variabile din programul Pascal cu valori preluate din fisierul extern.

Citirea se face diferit pentru fiecare clasa de fisiere Borland (Turbo) Pascal.Ea poate sau nu sa fie Œnsotita de conversii.

9.4.2.6. Scrierea

Operatia de scriere Œnseamna transferarea de informatie din memoria interna acalculatorului Œn fisierul extern. Mai exact, scrierea realizeaza memorareavalorilor unor variabile din programul Pascal Œn fisierul extern.

Scrierea se face diferit pentru fiecare clasa de fisiere Borland (Turbo)Pascal. Ea poate sau nu sa fie Œnsotita de conversii.

9.4.3. Fisiere text

Fisierele text sunt fisiere speciale, care se pot citi sau edita de oriceeditor de texte standard. Un fisier text este o succesiune de caractere ASCIIorganizate Œn linii. Numarul de linii este variabil, iar fiecare linie contineun numar variabil de caractere. O linie se termina cu o combinatie speciala decaractere (de regula CR+LF, adica ASCII 10 + ASCII 13), iar sfƒrsitul defisier poate fi determinat cu ajutorul functiei EOF. Aceasta poate folosi:- functia FileSize (care determina numarul de caractere din fisier)- un caracter special de sfƒrsit de fisier (cu codul ASCII 26, recunoscut prin combinatia CTRL+Z de la tastatura).

Nu este obligatoriu, cel putin Œn Turbo Pascal, ca un fisier text sa continaterminatorul de sfƒrsit de fisier.

Mediile Borland si Turbo Pascal permit specificarea unui fisier text princuvƒntul cheie text, care are declaratia:

type text = file of char;

In Borland si Turbo Pascal sunt disponibile urmatoarele functii si procedurispecifice lucrului cu fisierele text:- Append (procedura)- EOLN (functie)- Flush (procedura)- Read (procedura)- ReadLn (procedura)- SeekEOF (functie)- SeekEOLN (functie)- SetTExtBuf (procedura)- Write (procedura)- WriteLn (procedura)

59

Page 60: Sub Program Are

In cele ce urmeaza, cu exceptia locurilor unde se face o referire explicita,prin var_fis vom desemna o variabila fisier de tip text, deschis Œn prealabil.

Procedura Append este specifica fisierelor text. Ea permite deschiderea unuifisier Œn adaugare, adica:- deschide fisierul Œn scriere- seteaza contorul de pozitie la sfƒrsitul fisierului.

Declaratia sa este:

procedure Append(var var_fis: Text);

unde var_fis este o variabila fisier de tip text, asociata Œn prealabilprintr-o procedura Assign unui fisier extern existent. Daca fisierul externnu exista, se produce o eroare de intrare-iesire. Daca var_fis desemneaza unfisier deja deschis, acesta se Œnchide Œn prealabil si apoi se executaoperatiile specifice lui Append.

Daca terminatorul de sfƒrsit de fisier CTRL+Z este prezent, contorul depozitie se seteaza pe pozitia acestuia. Prin urmare, dupa fiecare scriere Œnfisier acesta va contine la sfƒrsitul sau terminatorul de fisier.

Functia EOLN Œntoarce statutul de sfƒrsit de linie, adica EOLN- Œntoarce True daca : - caracterul curent din fisier (specificat prin contorul de pozitie) este un terminator de linie sau de fisier - Œntre caracterul curent si sfƒrsitul de linie nu exista decƒt spatii- Œntoarce False altfel.

Declaratia aceste functii este:

function EOLN [(var var_fis: Text) ]: Boolean;

Daca var_fis lipseste, se considera ca EOLN se refera la fisierul standard deintrare.

Procedura Flush realizeaza transferul fizic de informatie din bufferul unuifisier deschis Œn scriere (cu Rewrite sau Append) pe suport. Declaratia saeste:

procedure Flush(var var_fis: Text);

Scrierea pe un fisier text se realizeaza prin intermediul unui buffer. In modnormal, scrierea fizica se efectueaza numai atunci cƒnd bufferul este plin.Apelƒnd Flush, ne asiguram ca se efectueaza scrierea si cƒnd bufferul nu esteplin.

Procedura standard Read citeste valori dintr-un fisier text Œntr-una sau maimulte variabile. Citirea se efectueaza Œncepƒnd de la contorul de pozitie,avansƒndu-se spre sfƒrsitul fisierului. Se pot efectua conversii, Œn functiede tipul variabilelor prezente ca parametri ai lui Read. Declaratia proceduriieste:

60

Page 61: Sub Program Are

procedure Read( [ var var_fis: Text; ] V1 [, V2,...,Vn ] );

unde V1, V2, ..., Vn sunt variabile pentru care Read este Œn domeniul lor devizibilitate. Citirea se opreste la sfƒrsitul de linie sau de fisier, fara ase citi si aceste caractere speciale.

Procedura standard ReadLn este similara procedurii Read, cu exceptia faptuluica dupa terminarea citirii trece la linia urmatoare: la Œntƒlnireacaracterelor de sfƒrsit de linie le sare, setƒnd contorul de pozitie dupa ele.Declaratia procedurii este:

procedure ReadLn( [ var var_fis: Text; ] V1 [, V2,...,Vn ] );

Dupa executarea lui ReadLn, contorul de pozitie va fi setat pe Œnceputul uneinoi linii.

Functia SeekEOF Œntoarce True daca s-a ajuns la sfƒrsitul de fisier si Falsealtfel. Declaratia sa este:

function SeekEOF [ (var var_fis: Text) ]: Boolean;

Functia SeekEOLN Œntoarce True daca s-a ajuns la sfƒrsit de linie si Falsealtfel. Declaratia sa este:

function SeekEOLN [ (var var_fis: Text) ]: Boolean;

Procedura SetTextBuf atribuie unui fisier text un buffer de intrare-iesire.Uzual, bufferul de I/E pentru fisierele text este de 128 de octeti. Cu cƒtbufferul este mai mare, cu atƒt operatiile de citire si de scriere se executamai rapid si printr-un numar mai mic de accese la suportul fizic. Declaratiaeste urmatoarea:

procedure SetTextBuf(var var_fis: Text; var Buf [ ; Lung: Word ] );

unde:- Buf este numele unei variabile (de obicei de tipul unui tablou de caractere) care va fi folosita pe post de buffer- Lung este dimensiunea bufferului (Œn octeti)

SetTextBuf trebuie apelata imediat dupa deschiderea fisierului (urmatoareainstructiune dupa Reset, Rewrite sau Append).

Procedura Write scrie valoarea uneia sau mai multor variabile Œntr-un fisiertext. Scrierea se efectueaza Œncepƒnd de la contorul de pozitie, avansƒndu-sespre sfƒrsitul fisierului. Se pot efectua conversii, Œn functie de tipulvariabilelor prezente ca parametri ai lui Write. Declaratia procedurii este:

procedure Write( [ var var_fis: Text; ] P1 [, P2,...,Pn ] );

unde P1, P2, ..., Pn sunt expresii de formatare, formate din nume devariabile sau expresii (de una din tipurile Char, Integer, Real, String si

61

Page 62: Sub Program Are

Boolean) ce contin variabile Œmpreuna cu specificatori de lungime si denumar de zecimale. Pentru tipurile numerice, se face conversia la stringŒnainte de scrierea Œn fisier. Fisierul trebuie sa fie deschis cu Rewritesau Append. Contorul de pozitie se va mari cu lungimea stringurilor scrise.

O expresie de formatare P are formatul: V:l[:z], unde- v este o variabila de tip numeric- l este lungimea stringului generat (daca partea Œntreaga a lui v are lungimea mai mare ca l, atunci l se va seta la lungimea partii Œntregi a lui v - luƒnd Œn considerare si semnul)- z este numarul de cifre la partea zecimala (numai pentru variabile reale).

Procedura WriteLn este similara cu Write, scriind la sfƒrsit un terminator delinie. Declaratia procedurii este:

procedure WriteLn( [ var var_fis: Text; ] P1 [, P2,...,Pn ] );

iar semantica ei este:

Write( var var_fis, P1 [, P2,...,Pn ] ); Write( var var_fis, Chr(13), Chr(10) ); { terminator de linie }

Procedurile ReadLn si WriteLn sunt specifice fisierelor text, pe cƒndprocedurile Read si Write se folosesc si Œn cazul fisierelor cu tip.

9.4.4. Fisiere cu tip

Fisierele cu tip (pe care le numim si fisiere record) sunt accesate prinintermediul unei variabile fisier declarata astfel:

var var_fis: file of tip_componenta;

sau, mai elegant:

type tip_componenta = ... { definitia tipului componentei } tip_fisier = file of tip_componenta;var var_fis: tip_fisier;

Fisierele cu tip respecta definitia generala a fisierului, precizata laŒnceputul acestei sectiuni: ele contin componente de acelasi tip, notat maisus prin tip_componenta. Tipul componentei poate fi orice tip recunoscut desistemul de tipuri al limbajului, cu exceptia tipului file.

Spre exemplu, urmatoarele tipuri de fisiere sunt tipuri valide:

type fisier_integer = file of Integer; fisier_boolean = file of Boolean; fisier_persoane = file of Persoana; { tipul Persoana din 9.3.4. }

62

Page 63: Sub Program Are

9.4.4.1. Bufferul fisierului

Operatiile efectuate asupra fisierelor cu tip sunt cele discutate general Œn9.4.2. Transferul de date Œntre suportul extern (fisierul extern) si memorie(variabilele din program) se realizeaza prin intermediul unei zone pe caream numit-o buffer (denumirea romƒneasca de pe vremuri era zona tampon).

Pentru un fisier declarat Œn forma:

var var_fis: tip_fisier;

bufferul este o zona de memorie speciala, fara nume, care o vom nota Œn celece urmeaza cu var_fis^. Initializarea variabilei fisier var_fis se face prinapelul procedurii standard Assign; deschiderea fisierului se face folosindprocedurile standard Reset (Œn citire sau Œn scriere si citire) si Rewrite(Œn scriere). Odata cu deschiderea, devine accesibil si bufferul fisierului,adica variabila var_fis^.

Bufferul trebuie considerat ca zona de memorie Œn care se citesc informatiiledin fisierul extern, Œnainte ca acestea sa fie atribuite variabilelor dinprogram; similar, Œn buffer sunt depozitate informatiile care se doreste a fiscrise Œn fisierul extern, Œnainte ca scrierea sa aiba loc. Ratiunea de a fia bufferului este aceea de a optimiza (Œn general de a micsora) numarul deaccese la suportul extern, pe care se gaseste fisierul supus prelucrarii.De obicei, operatiile fizice de citire si scriere pe suport extern realizeazatransferul unei cantitati fixe de informatie, numita bloc. Dimensiuneablocului depinde de caracteristicile fizice ale suportului si perifericuluipe care se memoreaza fisierul. Din punct de vedere logic, adica al fisieruluiprelucrat Œn programul Pascal, o citire sau scriere logica realizeazatransferarea unei cantitati de informatie egala cu dimensiunea unei componente afisierului, adica SizeOf(tip_componenta). De regula, dimensiunea bufferuluieste un multiplu al dimensiunii bloc

9.4.4.2. Citirea prin intermediul bufferului

Pentru a simplifica lucrurile, consideram ca dimensiunile blocului,bufferului si componentei sunt egale. Folosind declaratia:

type tip_componenta = Integer; tip_fisier = file of tip_componenta;var var_fis: tip_fisier; componenta: tip_componenta;

vom deschide acum Œn citire fisierul TEST.DAT si vom putea accesa deja primacomponenta a lui:

Begin Assign(var_fis, 'TEST.DAT');

63

Page 64: Sub Program Are

Reset(var_fis); componenta := var_fis^End.

Deschiderea provoaca si setarea contorului de pozitie pe prima Œnregistrare.Pentru a trece la urmatoarea componenta (adica pentru a aduce Œn bufferurmatoarea componenta) se foloseste o procedura speciala, numita get.

Semantica acesteia este:- aducerea Œn buffer a urmatoarei componente din fisier- marirea cu 1 a contorului de pozitie.

Prin urmare, daca dorim o prelucrare completa a fisierului (de exempluafisarea fiecarei componente pe ecran), atunci programul de mai sus s-arscrie astfel:

Begin Assign(var_fis, 'TEST.DAT'); Reset(var_fis); While not Eof(var_fis) do begin componenta := var_fis^; get(var_fis); WriteLn(componenta) end; Close(var_fis)end.

Am vazut Œnsa ca Œn Pascal exista procedura standard Read pentru citirea dinfisier. De fapt, semantica exacta a procedurii Read este data Œn tabelulurmator:

-------------------------------------------------------Procedura standard Este echivalenta cu-------------------------------------------------------Read(var_fis, componenta) componenta := var_fis^; get(var_fis);-------------------------------------------------------

Procedura get detecteaza sfƒrsitul de fisier: cƒnd nu mai exista o urmatoareŒnregistrare de citit, ea nu va Œntoarce nimic, iar functia Eof(var_fis) vaŒntoarce True, deci citirea fisierului se termina.

Folosind procedura standard Read, programul de mai sus se scrie astfel:

Begin Assign(var_fis, 'TEST.DAT'); Reset(var_fis); While not Eof(var_fis) do begin Read(var_fis, componenta); WriteLn(componenta) end; Close(var_fis)

64

Page 65: Sub Program Are

end.

9.4.4.3. Scrierea Œn fisier

Folosind aceleasi declaratii:

type tip_componenta = Integer; tip_fisier = file of tip_componenta;var var_fis: tip_fisier; componenta: tip_componenta; i: Integer;

vom deschide acum Œn scriere fisierul TEST.DAT si vom pune prima componenta alui Œn buffer:

Begin Assign(var_fis, 'TEST.DAT'); Rewrite(var_fis); var_fis^ := componentaEnd.

Deschiderea Œn scriere provoaca stergerea fisierului (daca acesta exista) sisetarea contorului de pozitie pe 0. Pentru a scrie o componenta Œn fisiereste nevoie ca aceasta sa fie trecuta prima data Œn buffer (lucru realizat deultima instructiune din program), dupa care se foloseste o procedura speciala,numita put. Semantica acesteia este:- scrierea continutului bufferului Œn fisier- marirea cu 1 a contorului de pozitie.Prin urmare, daca dorim o prelucrare completa a fisierului (de exempluscrierea fiecarei componente Œn el), atunci programul de mai sus s--ar scrieastfel:

Begin Assign(var_fis, 'TEST.DAT'); Rewrite(var_fis); For i := 1 to 10 do begin componenta := i; var_fis^ := componenta; put(var_fis) end; Close(var_fis)end.

Am vazut Œnsa ca Œn Pascal exista procedura standard Write pentru scrierea Œnfisier. De fapt, semantica exacta a procedurii Write este data Œn tabelulurmator:

-------------------------------------------------------Procedura standard Este echivalenta cu-------------------------------------------------------

65

Page 66: Sub Program Are

Write(var_fis, componenta) var_fis^ := componenta; put(var_fis);-------------------------------------------------------

In scriere, sfƒrsitul de fisier trebuie marcat. Acest lucru este efectuat deprocedura standard Close.

Folosind procedura standard Write, programul de mai sus se scrie astfel:

Begin Assign(var_fis, 'TEST.DAT'); Rewrite(var_fis); For i := 1 to 10 do begin componenta := i; Write(var_fis, componenta) end; Close(var_fis)end.

9.4.5. Fisiere fara tip (stream)

Fisierele fara tip pot fi considerate ca fiind fisiere cu tip in care oinregistrare are un octet, si tipul ei este Byte

type file = file of byte;

Deoarece este greoaie manipularea inregistrarilor de 1 byte, la fisierelefara tip se foloseste un parametru nou, dimensiunea inregistrarii (bufferului),numit in engleza RecSize. Acest parametru se precizeaza la deschidereafisierului. Daca nu este precizat, se considera implicit valoarea 128.Procedurile standard Reset si Rewrite au forma generala:

procedure Reset(var var_fis [: File; Recsize: Word ] ); procedure Rewrite(var F: File [; Recsize: Word ] );

Pentru citirea si scrierea din fisierele fara tip se folosesc procedurispecifice, BlockRead si BlockWrite.

Procedura BlockRead are sintaxa:

procedure BlockRead(var F: File; var Buf; Count: Word [; var Result: Word]);

unde: F Variabila fisier fara tip Buf variabila de orice tip (de obicei un tablou de byte), de lungime cel putin egala cu RecSize Count expresie de tip Word Result variabila de tip Word

Semantica acestei proceduri este urmatoarea: se citesc cel mult Countinregistrari (de lungime RecSize fiecare) din fisierul F in variabila Buf

66

Page 67: Sub Program Are

(care joaca rolul bufferului). Numarul efectiv de inregistrari citite esteintors in parametrul optional de iesire Result. Daca acesta lipseste, sedeclanseaza o eroare de intrare/iesire (detectabila prin folosirea luiIOResult) daca numarul de inregistrari citite este mai mic decat Count.Pentru ca operatia sa aiba sens, dimensiunea bufferului Buf trebuie safie cel putin egala cu Count * RecSize octeti, dar nu mai mare decat 64K:

65535 ò> SizeOf(Buf) ò> Count * RecSize

Daca Count * RecSize > 65535 se declanseaza o eroare de intrare/iesire.

Parametrul de iesire Result va avea valoarea (daca este prezent in apel) - egala cu Count daca din fisier s-a putut transfera numarul de octeti precizat - mai mic decat Count daca in timpul transferului s-a detectat sfarsitul de fisier; Result va contine numarul de inregistrari complete citit

Dupa terminarea transferului, contorul de pozitie al acestuia se marestecu Result inregistrari.

Procedura BlockWrite are sintaxa:

procedure BlockWrite(var F: File; var Buf; Count: Word [; var Result: Word]);

unde: F Variabila fisier fara tip Buf variabila de orice tip (de obicei un tablou de byte), de lungime cel putin egala cu RecSize Count expresie de tip Word Result variabila de tip Word

Semantica acestei proceduri este urmatoarea: se scriu cel mult Countinregistrari (de lungime RecSize fiecare) din variabila Buf in fisierul F.Numarul efectiv de inregistrari scrise este intors in parametrul optional deiesire Result. Daca acesta lipseste, se declanseaza o eroare de intrare/iesire(detectabila prin folosirea lui IOResult) daca numarul de inregistrari scriseeste mai mic decat Count.

Pentru ca operatia sa aiba sens, dimensiunea bufferului Buf trebuie safie cel putin egala cu Count * RecSize octeti, dar nu mai mare decat 64K:

65535 ò > SizeOf(Buf) ò >= Count * RecSize

Daca Count * RecSize > 65535 se declanseaza o eroare de intrare/iesire.

Parametrul de iesire Result va avea valoarea (daca este prezent in apel) - egala cu Count daca s-a putut transfera in fisier numarul de octeti precizat - mai mic decat Count daca suportul pe care se face transferul se umple; Result va contine numarul de inregistrari complete scris

Dupa terminarea transferului, contorul de pozitie al acestuia se mareste

67

Page 68: Sub Program Are

cu Result inregistrari.

9.4.6. Fisierele sistem

Sistemul de operare MS-DOS are doua fisiere standard: de intrare (tastatura),de unde se preia informatia de prelucrat, si de iesire (ecranul monitorului),unde se afiseaza rezultatul prelucrarilor.

In versiunea standard a limbajului Pascal cele doua fisiere se specifica Œnantetul programului:

program Nume(input, output);

In Turbo Pascal, aceasta specificare este implicita si poate sa lipseasca.Unit-ul System contine doua variabile fisier de tip text, cu numelerespective:

Var Input: Text { Input standard file } Output: Text { Output standard file }

In Unit-ul Printer, exista o variabila fisier de tip text care desemneazaimprimanta sistem:

var Lst : text; { System printer }

Algoritmica si programare - sem 2.Capitolele 3-6 - Structuri dinamice de date3.1. Tipul pointer in Pascal3.2. Liste simplu inlantuite3.3. Stive3.4. Cozi3.5. Structuri liniare3.6. Arbori3.1. Tipul pointer in Pascalvezi POINTER.LEC - directorul POINTER

4 Liste simplu inlantuitevezi LISTE.LEC - directorul LISTE

5.1. Stivevezi STIVE.LEC - directorul STIVE

5.2. Cozivezi COZI.LEC - directorul COZI

5.3. Structuri liniarevezi STRLIN.LEC - directorul STRLIN

6. Arborivezi ARBORI.LEC - directorul ARBORI

68

Page 69: Sub Program Are

1. Programarea modulara.

1.1. Termenul de modul1.2. Structura unui modul1.3. Exemple1.4. Cum s-a ajuns la programarea modulara

1.1. Termenul de modul

Termenul de modul a fost folosit de-a lungul timpului in maimulte acceptiuni - subprogram (sursa sau obiect) - biblioteca de subprograme - colectie de subprograme destinate unui anumit domeniu sau de uz general - exemple - biblioteca matematica a firmei IBM - IBM SSP (Scientific Subroutine Package) - biblioteca matematica a calculatorului Felix MATHLAB - biblioteca matematica a firmei DEC - modul obiect - rezultat din compilarea unui modul sursa - modul executabil (DLL) - fisier ce contine cod executabil (functii) si este incarcat cand o aplicatie apeleaza o functie din el DLL - Dynamic Link Library, biblioteca cu legare dinamica

Ce intelegem prin modul - unitate de program - contine declaratii de - subprograme - tipuri de date - variabile - constante - etc - declaratiile sunt - publice (exportate) - se pot folosi de clientii modulului - private (interne) - se pot folosi numai de modul - se compileaza separat - faciliteaza reutilizarea codului - respecta principiul ascunderii informatiei - datele private ale modulului sunt accesibile numai prin intermediul subprogramelor publice ale acestuia.

Ideile programarii modulare - sa se grupeze subprogramele in functie de datele pe care le folosesc - sa se protejeze datele de accesul neautorizat prin 'ascunderea' lor in zona privata a modulului - o mai buna diviziune a muncii de programare - module server: mai complexe, mai dificil de implementat - module client: folosesc declaratiile publice din modulele

69

Page 70: Sub Program Are

server

Exemplu de modul: unit-ul Turbo (Borland) Pascal

1.2. Structura unui modul

Un modul are urmatoarele parti - antet - parte de interfata (publica) - parte de implementare (privata) - cod de initializare

1.2.1. Antetul

- precizeaza numele modulului - numele modulului apare in frazele Uses din modulele client

Unit Nume_Modul;

- Unit este un cuvant rezervat - restrictii: - Nume_Modul sa fie identic numelui fisierului sursa (Nume_Modul.PAS) - Uses provoaca cautarea unui modul Nume_Modul.TPU

1.2.2. Partea de interfata

Partea de interfata contine declaratiile numelor (simbolurilor) publice.Declaratiile se refera la - modulele server (o fraza Uses) - constante - tipuri de date - variabile - subprograme (proceduri si functii)

Toti clientii unit-ului pot folosi numele care sunt declarate in aceastaparte. In situatia in care exista conflict de nume, numele in conflict secalifica cu numele modulului:

Nume_Modul.Nume

Sintaxa partii de interfata este:

interface { declaratii de nume publice }: uses { ce module se folosesc in partea de interfata } const { declaratii de constante } type { declaratii de tipuri de date } var { declaratii de variabile } procedure { antete de proceduri } function { antete de functii }

1.2.3. Partea de implementare

70

Page 71: Sub Program Are

Partea de implementare este obligatoriu sa contina corpul subprogramelor(proceduri si functii) declarate in partea de interfata. In plus, aceastaparte poate contine si obiecte (etichete, constante, tipuri de date,variabile, subprograme) locale. Termenul de "local" inseamna ca acesteobiecte sunt folosibile numai in partea de implementare, nefiind vizibilein afara acesteia.

Sintaxa partii de implementare este:

implementation { declaratii de nume locale }: uses { ce module se folosesc in implementare } label { declaratii de etichete locale } const { declaratii de constante locale } type { declaratii de tipuri de date locale } var { declaratii de variabile locale } procedure { corpul procedurilor publice } function { corpul functiilor publice } procedure { corpul procedurilor locale } function { corpul functiilor locale }

Pentru subprogramele publice nu este necesara specificarea parametrilor.Declaratia completa a lor este obligatoriu sa fie facuta in partea deinterfata. Daca se specifica parametrii si in partea de implementare, antetulsubprogramului din partea de implementare trebuie sa fie identic (textual) cucel dat in partea de interfata.

Subprogramele publice se pot apela in partea de implementare fara a respectaregula: declarare, apoi apelare. Acest lucru este posibil deoarece declararealor in partea de interfata actioneaza ca o declaratie "forward".

Subprogramele locale (private) trebuie sa respecte regulile uzuale: declarare,apoi apelare.

1.2.4. Codul de initializare

Codul de initializare a modulului are ca ca functia principala initializareavariabilelor (locale in special) din modul. Daca nu este nevoie de unasemenea cod, se pune pur si simplu cuvantul rezervat "end."Daca trebuie cod de initializare, acesta se va pune intre begin si end.

Din acest punct de vedere, codul de initializare a unui modul poate fiasimilat programului principal Pascal (care are instructiunile intrebegin si end.).

1.3. Exemple:

Unit-urile standard Crt Dos Printer

Client Module folosite

71

Page 72: Sub Program Are

Complexe UComplex Test UDataC Crt Calendar UDataC CitireT UFile

UPolinom - rez.de ecuatii de gr I si II

1.4. Cum s-a ajuns la programarea modulara1.4.1. Reutilizarea codului1.4.2. Biblioteci de subprograme1.4.3. Deficientele bibliotecilor de subprograme1.4.4. Principiul programarii modulare1.4.5. Deficientele programarii modulare

1.4.1. Reutilizarea codului

La putin timp dupa aparitia limbajelor de nivel inalt a aparut si conceptulde subprogram, si odata cu acesta, principiul abstractizarii procedurale.Conform acestui principiu, o secventa de instructiuni care efectueaza oaceeasi prelucrare se pot grupa intr-o unitate de program independenta,numita subprogram, care se caracterizeaza prin: - un nume (cu care se apeleaza) - o lista de parametri formali (de intrare si de iesire), care atribuie subprogramului un caracter general (rezolva o clasa de probleme definita de parametrii sai)

In orice loc in care este nevoie de executia instructiunilor cuprinse incorpul subprogramului, nu este nevoie sa se rescrie acele instructiuni.Este nevoie doar de o instructiune de apel a subprogramului, care contine - numele acestuia - lista de parametri actuali (sau efectivi), care trebuie sa corespunda listei parametrilor formali ai subprogramului

Pentru a putea fi refolosit in programe diferite, codul sursa al unuisubprogram nu trebuie sa refere nici un nume din mediul sau de apel (adicade acolo unde a fost scris prima data). Rezulta de aici ca in corpulunui subprogram: - nu se folosesc variabile globale - nu se folosesc nici un alt fel de declaratii exterioare subprogramului - toate numele folosite sunt fie nume de parametri, fie nume declarate local in subprogram.

Aceasta este prima forma de reutilizare a codului sursa: codul sursa alunui subprogram este scris o singura data si folosit in orice loc estenevoie de el.

Limbajul Pascal standard rezolva problema reutilizarii codului prinincluderea textului sursa al unui subprogram (existent intr-un fisiersursa) in codul sursa al programului care-l apela (folosind o directivade includere). Mediile Turbo si Borland Pascal au pastrat si aceasta forma

72

Page 73: Sub Program Are

de reutilizare a codului prin directiva de compilare $I.

1.4.2. Biblioteci de subprograme

O maniera mai eleganta de grupare a subprogramelor este cunoscuta subnumele de "biblioteci de subprograme". Acestea sunt fisiere sursa carecontin codul sursa al mai multor subprograme. In unele limbaje precumFORTRAN, aceste fisiere se puteau compila separat, rezultand moduleobiect care se puteau pe urma lega cu programele ce le apelau, formandprin link-editare programe executabile.

Astfel de biblioteci sunt: - biblioteca matematica a calculatorului Felix C-256 (MathLib) - biblioteca stiintifica a firmei IBM (IBM SSP - Scientific Subroutine Package) - biblioteca matematica a firmei DEC

Utilizatorul unei astfel de biblioteci trebuia doar sa cunoasca modul deapelare a subprogramului dorit.

1.4.3. Deficientele bibliotecilor de subprograme

Intre deficientele folosirii bibliotecilor de subprograme mentionam: - ele nu contin decat subprograme - nu se face nici un fel de verificare a corectitudinii apelului (la compilare nu este nevoie de existenta bibliotecii; este sarcina link-editorului sa rezolve apelul de subprogram, avand codul compilat din modulul obiect, pe care-l include in programul executabil) - toata responsabilitatea folosirii corecte a apelului cade in sarcina programatorului client (cel care apeleaza subprogramul).

1.4.4. Principiul programarii modulare

Odata cu cresterea complexitatii programelor, a devenit din ce in cemai imperioasa problema protejarii datelor dintr-un program. In spetaeste vorba de variabilele globale ale unui program, care (cel putinteoretic) se pot modifica accidental si in partile programului care nu artrebui sa le modifice.

Tinand cont de observatia (reala) de mai sus, s-a enuntat principiulprogramarii modulare, care afirma urmatoarele: - grupeaza subprogramele in module, dupa principiul datelor comune - foloseste principiul ascunderii informatiei pentru aceste date.

Asa s-a ajuns de la biblioteci de subprograme (in care subprogrameleerau grupate de obicei pe criteriul functionalitatii) la module, in caresubprogramele se grupeaza pe criteriul datelor pe care trebuie sa le accesezesau sa le modifice. Aceste date vor fi considerate locale modulului (deciinvizibile in afara lui), iar subprogramele care le prelucreaza vor ficonsiderate publice.

Gruparea procedurilor in module pe criteriul datelor comune rezolva deci

73

Page 74: Sub Program Are

- protectia datelor (care sunt invizibile in afara modulului) - reutilizarea modulului (impreuna cu datele din el) oriunde este nevoie de functionalitatea lui - o mai fina structurare a programelor client (o alta paradigma de programare).

1.4.5. Deficientele programarii modulare

Pana la un punct, intr-un modul putem concentra definitia unui nou tipde date, impreuna cu operatiile pe acest tip. Am spus pana la un punct,deoarece daca declaram un tip de date nou, declaratia trebuie sa fiepublica - deci in partea de interfata. O declaratie publica de tip are cadeficienta faptul ca orice variabila de tipul respectiv este modificabila(toate componentele sale sunt accesibile) - deci se incalca pe jumatateprincipiul ascunderii informatiei).

Exemplul unit-ului UDataC este edificator: el declara un tip de datanou, DataC (public) si implementeaza o serie de operatii pe el (si nu numai): - Compara - ZiCorecta - ZiuaDinAn - NumarZile - Aduna - ZiLucratoare - DataCToStr8 - DataCToStr10 - DataCToStr - AdunaZi - ScadeZi

Toate aceste operatii sunt utile, insa unit-ul UDataC nu ascunde "structura"DataC, deci nu se elimina posibilitatea modificarii accidentale (sauneautorizate) a unei variabile de tip DataC in programele client aleunit-ului.

O solutie ar fi renuntarea la o declaratie de tip nou si ascundereastructurii acestuia in partea de implementare a modulului. Pentru unit-ulDataC acest lucru ar insemna sa renuntam la toate declaratiile de tipuri dinpartea de interfata, sa le includem in partea de implementare si sa modificamaproape toate antetele subprogramelor (pentru ca nu mai avem tipul DataC).Nu am castiga mai nimic in ceea ce priveste protectia datelor, insa amcastiga sigur in neclaritate.

Exista si situatii cand putem ascunde structura tipului in partea deimplementare. Unit-ul UListCCD.PAS reprezinta, impreuna cu programul sauclient, LSTCapCd.PAS un exemplu de modul corect gandit, in spiritulparadigmei programarii modulare.

In unit-ul UListCCD au fost incluse subprograme care opereaza cu o structurade date dinamica numita ListaCapCoada. Declaratia tipului, ca si aunicei variabile de tipul respectiv sunt incluse in partea de implementare amodulului, deci nu sunt vizibile in afara acestuia:

74

Page 75: Sub Program Are

type ListaCapCoada = Record Cap, { capul listei: folosit la traversare } Coada: PNod { coada listei: acolo se fac inserari si stergeri } End;var L: ListaCapCoada;

Deoarece lucreaza toate cu variabila (locala) L, subprogramele publice - Creeaza: Crearea unei liste vide - Insereaza: Adaugarea nodului N in coada listei - Sterge: Stergerea nodului din coada listei - Cauta(I): Cauta in lista nodul cu informatia utila I - Traverseaza: Traverseaza toate nodurile listei si afiseaza informatia utila din ele - Elibereaza: Dealoca toate elementele listei, transformand-o intr-o lista vidanu au nevoie de L ca parametru. Ca fapt divers, aici am incalcat principiulexpus mai sus, al autonomiei subprogramelor. De data aceasta, deoarece elesunt grupate in acest modul pe principiul datelor comune care le folosesc(in cazul nostru variabila L), nu este nevoie ca acest principiu sa fierespectat: daca datele si implementarea subprogramelor ce actioneaza pe elesunt ascunse exteriorului, nu este nici un pericol in a altera aceste datedin afara.

In acest exemplu am ilustrat principiul programarii modulare. Exemplulfunctioneaza corect atata timp cat in programele client avem nevoie de osingura lista. Daca insa am avea nevoie de doua liste, modulul nostru nu maie bun. Aceasta este de fapt principala deficienta a programarii modulare: - datele locale in modul permit lucrul cu o singura instanta a modulului (considerat ca tip de date).

A fost nevoie de urmatoarea paradigma de programare - abstractizarea datelor sau - programarea bazata pe obiectepentru a rezolva aceasta problema.

2. Abstractizarea datelor2.1. Istoric2.2. Ce este abstractizarea datelor2.2.1. Tipuri de date definite de utilizator2.2.2. Nivele de abstractizare a tipurilor de date2.3. Tipuri abstracte de date2.3.1. Specificarea tipurilor abstracte de date2.3.2. Proiectarea operatiilor unui TAD2.3.3. Implementarea tipurilor abstracte de date2.3.4. Avantajele TAD2.4. Exemple

2.1. Istoric

75

Page 76: Sub Program Are

Date Operatii Imbunatatire -------------------------------------------------------------- adrese de memorie instructiuni masina performanta

celule de memorie instructiuni in lizibilitate cu nume limbaj de asamblare mai usor de (mnemonice) inteles

tipuri de date instructiuni standard independenta de standard (secventa, decizie, masina repetitie)

tipuri de date subprograme limbaje virtuale utilizator \ / \ / module ascunderea informatiei

tipuri abstracte de date instante multiple (abstractizarea datelor) (programare bazata pe obiecte)

clase polimorfism dinamic (programare orientata pe mostenire obiecte) legare dinamica

2.2. Ce este abstractizarea datelor2.2.1. Tipuri de date definite de utilizator2.2.2. Nivele de abstractizare a tipurilor de date

Abstractizarea inseamna - neglijarea detaliilor nesemnificative - concentrarea asupra esentei.

2.2.1. TDU - Tipuri de date definit de utilizator

Am vazut ca limbajul Pascal ne permite, prin intermediul declaratiei 'type', sa definim tipuri de date proprii, altele decat cele standard. Stim ca un tip de date T este caracterizat de - domeniul valorilor (multimea valorilor pe care le poate lua o variabila de tipul T); domeniul este precizat prin componentele noului tip definit - enumerare - subdomeniu - multime - tablou - record - multimea operatiilor care se pot efectua asupra variabilelor de tipul T.

Din pacate, daca Pascal acorda atentie speciala numai definirii domeniului unui nou tip T, lasand nerezolvata problema definirii

76

Page 77: Sub Program Are

operatiilor pe noul tip T. Cu exceptia tipurilor multime, toate celelalte tipuri definite de utilizator au ca operatii predefinite: - atribuirea (componenta cu componenta) - testul de egalitate (componenta cu componenta).

Exemple: Type T = ... tip definit de utilizator var a1,a2: T; a1 si a2 sunt variabile de tipul T

- atribuirea (componenta cu componenta) - T de tip tablou: a1 := a2 inseamna a1[i] := a2[i] pentru i luand valori de la cel mai mic indice pana la cel mai mare indice - T de tip record: a1 := a2 inseamna a1.c1 := a2.c1; c1, c2, ..., cn sunt campurile din a1.c2 := a2.c2; declaratia lui T ... a1.cn := a2.cn; - testul de egalitate (componenta cu componenta) - T de tip tablou: a1 = a2 inseamna a1[i] = a2[i] pentru i luand valori de la cel mai mic indice pana la cel mai mare indice - T de tip record: a1 = a2 inseamna a1.c1 = a2.c1; c1, c2, ..., cn sunt campurile din a1.c2 = a2.c2; declaratia lui T ... a1.cn = a2.cn;

2.2.2. Nivele de abstractizare a tipurilor de date

Notiunea de tip de date este discutata aici din punctul de vedereal programarii. In fond, exista doua entitati care coopereaza: utilizatoruluman, ce poseda inteligenta si foloseste un limbaj specific speciei umanesi masina fizica (calculatorul), care executa un program format dininstructiuni pe care el le Œntelege. Intermediarul Œntre aceste entitatieste o masina virtuala, necesara unei comunicari cƒt mai lesnicioase Œntreele, cu alte cuvinte un limbaj de programare.

Se pot distinge trei nivele de abstractizare ale unui TD simplu saustructurat: abstract, virtual si fizic. Ordinea de enumerare a acestornivele pleaca de la utilizatorul uman si are ca destinatie masina fizicape care se lucreaza, calculatorul.

Plecƒnd de la rationamentul uman, primul nivel este cel abstract,concretizat Œn ceea ce numim tip abstract de date TAD. Abstractizarea arerolul de a pune accent pe proprietatile esentiale ale TD (vizƒnd atƒtdomeniul, cƒt si operatiile), neglijƒnd detaliile de reprezentare sialgoritmii necesari pentru realizarea concreta a operatiilor. Altfel spus,TAD pune accent pe semantica TD, Œn mod esential pe comportamentulacestuia, precizƒnd CE trebuie sa faca fiecare operatie si NU CUMactioneaza aceasta.

77

Page 78: Sub Program Are

Abstractizarea poate fi vazuta si ca o modalitate de ordonare agƒndirii umane. Conceperea unui TAD Œnseamna: - identificarea TAD (Œn termenii rationamentului uman), - stabilirea unui nume (care sa sintetizeze proprietatile sale), - precizarea domeniului si a operatiilor.

Definirea domeniului TAD Œnseamna enumerarea proprietatilor pe caretrebuie sa le Œndeplineasca valorile TAD, iar definirea fiecarei operatiio : E -> F Œnseamna: - stabilirea conditiilor de utilizare corecta a ei, - stabilirea domeniului E (parametrii de intrare) si a codomeniului F (parametrii de iesire), precum si - precizarea rezultatului ei.

Nivelul abstract este cel mai apropiat de definitia datei, vazuta caelement purtator de informatie. La acest nivel, detaliile de reprezentare avalorilor posibile ale ei (domeniul TD) nu sunt precizate, esentiale fiindoperatiile, adica comportamentul, functionalitatea datei, posibilitatile devalorificare a informatiei continuta Œn data.

Definirea TAD poarta numele de specificare.

Nivelul intermediar de abstractizare este cel virtual, Œn carese executa urmatoarele actiuni: - se alege un limbaj de programare LP (masina virtuala); - se defineste reprezentarea TAD Œn termenii LP; Niklaus Wirth: Algorithms + Data Structures = Programs Prentice-Hall, 1976 - se proiecteaza algoritmii necesari pentru operatii cu ajutorul reprezentarii alese; - se scrie codul sursa pentru operatiile TAD sub forma de subprograme Œn LP.

Masina virtuala exista numai ca o combinatie Œntre compilatorul(sau interpretorul) LP, sistemul de operare si arhitectura hard acalculatorului gazda. LP poseda TD proprii si/sau mecanisme de tipizare,precum si un set de instructiuni, cu ajutorul carora se pot exprimareprezentarea si operatiile TD concepute la nivelul abstract.La nivelul virtual putem vorbi de o concretizare a TAD, obtinƒndu-se untip virtual de date TVD. Trecerea de la TAD la TVD este numitaimplementare sau codificare.

Ultimul nivel de abstractizare este cel fizic, Œn care componenteleTVD sunt exprimate folosind resursele fizice ale calculatorului (memoriesi instructiuni cablate), ca rezultat al procesului de traducere a TVD.Masina fizica poate fi considerata si ea ca un TD, Œn care domeniul arfi memoria, iar operatiile ar fi instructiunile cablate. Din punct de vederefizic, memoria poate fi vazuta ca un vector de octeti (octetul fiindunitatea adresabila), iar instructiunile cablate sunt singurele operatiipe care masina stie sa le execute, ele formƒnd limbajul masina. Acest modelde TD Œl vom numi tip fizic de date TFD. Trecerea de la TVD la TFD serealizeaza prin traducerea programului, cu ajutorul unui translator

78

Page 79: Sub Program Are

(compilator, asamblor sau interpretor) si eventual a unui editor delegaturi.

Rezumƒnd, cele trei nivele de abstractizare ale unui TD sitransformarile de trecere sunt prezentate Œn figura 1.

OM MASINA ÄÄÄÄÄÄÄÄÄÄÄ> TAD ÄÄÄÄÄÄÄÄÄÄÄÄ> TVD ÄÄÄÄÄÄÄÄÄÄÄÄ> TFD specificare implementare traducere

Figura 1. Nivele de abstractizare a unui tip de date

Fiecarui nivel de abstractizare Œi corespunde un limbaj Œn caretrebuiesc precizate componentele TD, adica domeniul si operatiile,conform tabelului 1.

ÚÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Nivel ³ Limbaj ³ ÃÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ abstract ³ de specificare ³ ³ virtual ³ de programare ³ ³ fizic ³ masina ³ ÀÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Tabelul 1. Nivele de abstractizare si limbaje

Limbajul de specificare a TAD poate fi formal sau informal. Limbajulformal este riguros, exact, dar greu de Œnteles de nespecialisti (facƒndapel la notatii matematice), pe cƒnd limbajul informal este mult maiapropiat de exprimarea umana, chiar daca el sufera Œn rigoare si exactitate.Specificarea formala a TAD utilizeaza Œn esenta mecanisme algebrice(algebre universale, algebre multisortate, ä-algebre), reprezentƒnd astazio directie importanta de cercetare Œn informatica teoretica. Limbajul de programare, vazut ca mijloc de exprimare la nivelulvirtual de abstractizare, poate fi mai apropiat de masina fizica (limbaj deasamblare) sau de utilizatorul uman (limbaj de nivel Œnalt). In ambeleipostaze, folosirea lui este conditionata de existenta unui translatorcapabil sa traduca TVD Œntr-un program executabil pe masina fizica.

Traducerea TVD Œnseamna exprimarea lui (ca reprezentare si operatii)Œn limbaj masina. Ca programe, TVD si TFD sunt semantic echivalente (adicaproduc acelasi rezultat), deosebirea dintre ele constƒnd doar Œn mijloacelede exprimare, adica Œn limbajul folosit.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Nivel ³ Nivel virtual ³ Nivel fizic ³³ abstract ³ (Turbo Pascal) ³ (procesor 80x86) ³ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´³ Adresa ³ Record ³ Implementarea ³³ ³ Localitate:string[20]; ³ TD Record pe ³

79

Page 80: Sub Program Are

³ ³ Strada:String[10]; ³ IBM-PC ³³ ³ Numar:Integer ³ 34=21+11+2 oct. ³³ ³ End; ³ ³³ ³ ³ ³³ Temperaturi ³ Array[1..12] of Real; ³ Implementarea ³³ medii ³ ³ TD Array pe ³³ lunare ³ ³ IBM-PC ³³ ³ ³ 12*6=72 octeti ³³ ³ ³ ³³ Vƒrsta ³ Integer ³ Œntreg 80x86 ³³ ³ ³ 2 octeti ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Figura 2. Tipuri de date si nivele de abstractizare

2.3. Tipuri abstracte de date (TAD)

2.3.1. Specificarea tipurilor abstracte de date

Asa cum am aratat mai inainte, TAD se precizeaza cu ajutorul unuiasa-numit limbaj de specificare, formal sau informal. In cele ce urmeazavom folosi un limbaj de specificare informal, cu ajutorul caruia vom definischeme de specificare pentru TD simple si pentru TD structurate. In esenta,o schema de specificare a TAD contine doua parti: specificarea domeniuluiTD si specificarea operatiilor. In cazul TD structurate, specificareadomeniului TD Œnseamna: - specificarea elementelor (descrierea elementelor componente), - specificarea structurii (adica a relatiei sau legaturii dintre elemente) - specificarea propriu-zisa a domeniului (descrierea multimii valorilor posibile).

Specificarea unei operatii se realizeaza Œn maniera procedurala,operatiei atasƒndu-i-se un nume, o lista de parametri, ea furnizƒnd unrezultat. De asemenea, pentru fiecare operatie trebuiesc precizatepreconditia si postconditia. Preconditia este o asertiune (afirmatie)care precizeaza Œn ce conditii operatia are sens si ea trebuie verificatainainte, pentru ca operatia sa se execute corect, iar postconditia exprimalegaturile dintre rezultatele obtinute Œn urma executarii operatiei sioperanzi Œn ipoteza ca preconditia este verificata. Este important de precizat ca postconditia specifica numai CErezultate se obtin, fara a intra Œn detalii privind CUM se obtin acestea.In figura 3 prezentam schemele de specificare pentru TAD simplu, TADstructurat si pentru o operatie.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ a) Schema de specificare a unui TAD simplu ³³ ³³ Domeniu: descrierea multimii valorilor posibile ³³ ³³ Operatii: specificarea fiecarei operatii, vezi c) ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

80

Page 81: Sub Program Are

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ b) Schema de specificare a unui TAD structurat ³³ ³³ Elemente: descrierea elementelor componente ³³ ³³ Structura: descrierea relatiilor sau legaturilor Œntre ³³ elemente ³³ ³³ Domeniu: descrierea multimii valorilor posibile ³³ ³³ Operatii: specificarea fiecarei operatii, vezi c) ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ c) Schema de specificare a unei operatii ³³ ³³ Nume: numele operatiei (identificator, cƒt mai sugestiv) ³³ ³³ Lista de parametri: specificarea parametrilor, ca ³³ perechi (identificator, TD) ³³ ³³ Rezultat: (cƒnd operatia are sensul unei functii Pascal) ³³ TD al rezultatului Œntors ³³ ³³ Pre: preconditia (eventuale conditii impuse parametrilor ³³ de intrare) ³³ ³³ Post: postconditia (precizarea legaturilor dintre ³³ rezultatele obtinute si parametrii ³³ de intrare) ³³ ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Figura 3. Scheme de specificare

Pentru exemplificare, vom specifica cƒteva TAD simple: Zile,Natural si Sutime si TAD structurat Monede.

Specificarea 1. Tipul abstract simplu Zile

Domeniu: multimea valorilor posibile este: SAP={luni,marti,miercuri,joi,vineri,sƒmbata,duminica}. Operatii: Ieri(z:Zile):Zile {Stabileste ziua precedenta a zilei z} Pre: zîSAP. Post: Ieri(z) este ziua precedenta a zilei z. Mƒine(z:Zile):Zile {Stabileste ziua ce urmeaza zilei z} Pre: zîSAP.

81

Page 82: Sub Program Are

Post: Mƒine(z) este ziua urmatoare a zilei z. Libera(z:Zile):Boolean {z este zi nelucratoare ?} Pre: zîSAP. Post: Daca z este zi nelucratoare. atunci Libera(z) este True altfel Libera(z) este False. Scrie(z:Zile) {scrie valoarea lui z la dispozitivul standard de iesire} Pre: zSAP. Post: Se va afisa ziua z ca sir de caractere. Citeste(var z:Zile) {citeste valoarea lui z de la dispozitivul standard de intrare} Pre: TRUE. Post: rezultatul z se va citi ca sir de caractere ce reprezinta o valoare din domeniul SAP.

Specificarea 2. Tipul abstract simplu Natural

Domeniu: multimea valorilor posibile este multimea N a numerelor naturale. Operatii: MaiMare(n1,n2:Natural):Boolean {n1 > n2 ?} Pre: n1,n2îN. Post: Daca n1 > n2 atunci MaiMare(n1,n2) va fi True altfel MaiMare(n1,n2) va fi False. Egal(n1,n2:Natural):Boolean {n1 = n2 ?} Pre: n1,n2îN. Post: Daca n1 = n2 atunci Egal(n1,n2) va fi True altfel Egal(n1,n2) va fi False. Aduna(n1,n2:Natural; var n:Natural) {calculeaza suma lui n1 si n2} Pre: n1,n2îN. Post: n = n1 + n2. Scade(n1,n2:Natural; var n:Natural) {scade n2 din n1} Pre: n1,n2îN si MaiMare(n1,n2). Post: n = n1 - n2. Ori(n1,n2:Natural; var n:Natural) {calculeaza produsul lui n1 si n2} Pre: n1,n2îN. Post: n = n1 * n2. Cƒt(n1,n2:Natural; var n:Natural) {calculeaza cƒtul Œmpartirii lui n1 la n2} Pre: n1,n2îN si n2>0. Post: n = partea Œntreaga a cƒtului n1 / n2. Rest(n1,n2:Natural; var n:Natural) {calculeaza restul Œmpartirii lui n1 la n2} Pre: n1,n2îN si n2>0. Post: n = n1 - Cƒt(n1,n2) * n2.

Specificarea 3. Tipul abstract simplu Sutime

Domeniu: multimea valorilor posibile este multimea numerelor naturale

82

Page 83: Sub Program Are

cuprinse Œntre 0 si 99, notata prin DS. Operatii: MaiMare(s1,s2:Sutime):Boolean {s1 > s2 ?} Pre: s1,s2îDS. Post: Daca s1 > s2 atunci MaiMare(s1,s2) va fi True altfel MaiMare(s1,s2) va fi False. Egal(s1,s2:Sutime):Boolean {s1 = s2 ?} Pre: s1,s2îDS. Post: Daca s1 = s2 atunci Egal(s1,s2) va fi True altfel Egal(s1,s2) va fi False. Aduna(s1,s2:Sutime; var n:Natural; var s:Sutime) {calculeaza suma lui s1 si s2} Pre: s1,s2îDS. Post: s va contine partea fractionara a sumei dintre s1 si s2 iar n va contine partea Œntreaga a acesteia (transportul). Scade(s1,s2:Sutime; var s:Sutime) {scade s2 din s1} Pre: s1,s2îDS si MaiMare(s1,s2) sau Egal(s1,s2). Post: s = s1 - s2.

Specificarea 4. Tipul abstract structurat Monede

Elemente: Elementele componente sunt partea Œntreaga, respectiv partea fractionara a unei sume monetare. Structura: Se noteaza cu Lei partea Œntreaga a sumei, respectiv cu Bani partea fractionara a acesteia. Exista o corespondenta unu-la-unu Œntre fiecare identificator si elementul component corespunzator. Domeniu: Valoarea partii Œntregi a sumei, Lei, este un numar natural, iar cea a partii fractionare, Bani, este Œn intervalul natural 0..99. Domeniul tipului Monede, DM este vazut ca produsul cartezian al acestor doua domenii ale elementelor componente. Operatii:

BaniInMonede(n:Sutime; var r:Monede) {transforma suma n exprimata Œn Bani Œn Lei si Bani} Pre: nîDS. Post: r este valoarea lui n exprimata Œn Lei si Bani. LeiInMonede(n:Natural; var r:Monede) {transforma suma n exprimata Œn Lei Œn Lei si Bani} Pre: nîN. Post: r este valoarea lui n exprimata Œn Lei si Bani. LeiDinMonede(s:Monede):Natural{partea Œntreaga a sumei} Pre: sîDM. Post: LeiDinMonede(s) este valoarea cƒmpului Lei al lui s. BaniDinMonede(s:Monede):Sutime {partea fractionara a sumei} Pre: sîDM. Post: BaniDinMonede(s) este valoarea cƒmpului Bani al lui s. MonedeInReal(s:Monede):Real {valoarea sumei s ca numar real} Pre: sîDM.

83

Page 84: Sub Program Are

Post: numarul real MonedeInReal(s) va avea partea intreaga egala cu Lei si partea fractionara egala cu Bani / 100. RealInMonede(r:Real; var s:Monede) {valoarea numarului real r convertita la tipul Monede} Pre: rîR. Post: sîDM are componentele: cƒmpul Lei este partea Œntreaga a numarului real r, cƒmpul Bani are ca cifre primele doua zecimale din partea fractionara a lui r. MaiMare(s1,s2:Monede):Boolean {s1 > s2 ?} Pre: s1,s2îDM. Post: Daca s1 > s2 atunci MaiMare (s1,s2) va fi True altfel MaiMare (s1,s2) va fi False. Egal(s1,s2:Monede):Boolean {s1 = s2 ?} Pre: s1,s2îDM. Post: Daca s1 = s2 atunci Egal (s1,s2) va fi True altfel Egal (s1,s2) va fi False. Aduna(s1,s2:Monede; var r:Monede) {aduna s1 si s2} Pre: s1,s2îDM. Post: r = s1 + s2. Scade(s1,s2:Monede; var r:Monede) {scade s2 din s1} Pre: s1,s2îDM si MaiMare(s1,s2). Post: r = s1 - s2. Scrie(s:Monede) {scrie valoarea lui s la dispozitivul standard de iesire} Pre: sîDM. Post: se va afi_a s Œn formatul '999999,99 lei'. Dobƒnda(s:Monede; d:Real; var r:Monede) {calculeaza dobƒnda r aferenta sumei s si ratei r} Pre: s DM si dR. Post: r = dobƒnda la suma s calculata cu rata dobƒnzii d.

2.3.2. Proiectarea operatiilor unui TAD

Cea mai importanta faza Œn specificarea unui TAD este proiectareamultimii operatiilor. Multimea operatiilor Œn ansamblul ei trebuie saŒndeplineasca urmatoarele conditii: (1) sa fie completa; (2) sa fie minimala.

Dupa criteriul functionalitatii lor, clasele de operatii ale unuiTAD sunt urmatoarele (clasificare data de Riley, 1987): (1) constructori; (2) operatii de intrare/iesire; (3) operatii de conversie; (4) operatii de test; (5) operatii de copiere; (6) operatii de selectare.

84

Page 85: Sub Program Are

Vom discuta Œn cele ce urmeaza caracteristicile fiecarei clase deoperatii. Exemplele furnizate apartin specificarii 4.

Constructorii sunt operatii prin care se atribuie valori unorrealizari ale TAD, adica creeaza sau modifica instante ale TAD. In cazulŒn care operatia se realizeaza prin utilizarea unor date de alt tip,constructorii se numesc initiali. Completitudinea setului de operatiise poate formula astfel:

Constructorii unui TAD trebuie sa produca orice element din domeniul acestuia.

Exemple de constructori: BaniInMonede, LeiInMonede (initiali), Aduna, Scade, Dobƒnda.

Operatiile de intrare-iesire (numite prescurtat operatii de I/O)permit citirea sau scrierea valorii unei realizari a TAD de pe/pe suportextern. Acest lucru este necesar deoarece, cu putine exceptii, TAD suntstructurate si majoritatea LP nu dispun de operatii de I/O cu care sase poata implementa TVD asociate TAD. Operatiile de intrare pot ficonsiderate si constructori. Scrie este o operatie de I/O (de iesire maiexact, deci nu este constructor).

Operatiile de conversie realizeaza transformarea valorii instantelorTD Œn valori de instante ale altor TD. Operatiile MonedeInReal siRealInMonede fac parte din aceasta clasa. RealInMonede se poateconsidera si constructor pentru Monede.

Operatiile de test sunt necesare Œn primul rƒnd pentru testareapreconditiilor, putƒnd fi folosite chiar Œn specificarea TAD (vezispecificarile 2-4). MaiMare si Egal sunt astfel de operatii.

Operatiile de copiere sunt necesare cƒnd nu se poate folosiinstructiunea de atribuire pentru a copia valoarea unei instante de TADŒntr-o alta instanta. Ele sunt considerate si constructori, numiti uneorichiar constructori de copiere (in C++). Pentru TAD Monede, o operatie

Copy(m1:Monede; var m2:Monede) { m2 := m1 }

ar fi o operatie de copiere care ar copia fiecare camp al instantei m1in campul corespunzator al instantei m2.

Operatiile de selectare permit (Œn cazul TAD structurate) operareacu elementele componente ale unei instante a TAD. LeiDinMonede siBaniDinMonede sunt astfel de operatii. Cu alte cuvinte, operatiile deselectare permit aflarea valorii unei componente a instantei (pentru TADstructurate), fara a permite modificarea ei.

O alta clasificare a operatiilor, data de Meyer (1988) distinge 3clase: - constructori (creeaza noi instante ale TAD), - accesori (permit accesul la valorile anumitor componente, fara a le

85

Page 86: Sub Program Are

modifica) si - modificatori (modifica anumite componente ale instantei).

Legatura dintre aceste doua clasificari este sintetizata Œn tabelul 2.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Meyer ³ Riley ³ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´³ constructori ³ constructori initiali ³³ ³ operatii de conversie spre TAD ³³ ³ operatii de intrare ³³ modificatori ³ constructori (altii decƒt cei ³³ ³ initiali) ³³ accesori ³ operatii de test ³³ ³ operatii de conversie de la TAD ³³ ³ operatii de selectare ³³ ³ operatii de iesire ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Tabelul 2. Tipuri de operatii pentru TAD

2.3.3. Implementarea tipurilor abstracte de date

Asa cum am precizat anterior, implementarea unui TAD presupuneparcurgerea urmatoarelor etape: - alegerea unui limbaj de programare LP, - stabilirea reprezentarii domeniului TAD Œn termenii acestui limbaj, - proiectarea algoritmilor necesari pentru implementarea operatiilor (tinƒnd cont de reprezentare) si, - scrierea de subprograme corespunzatoare fiecarei operatii, in LP.

In mod normal, faza de implementare mai contine o etapa de testare,vazuta atƒt la nivelul fiecarei operatii, cƒt si al TAD Œn ansamblu(testarea procedurala si testarea de modul). Rezultatul implementarii,tipul virtual de date, TVD, va fi numit Œn cele ce urmeaza modul saupachet, Œn ideea ca el este o unitate sintactica de sine statatoare,compilabila separat. In sectiunea 2.4. vom prezenta implementarile TADprezentate Œn specificarile 1 - 4.

2.3.4. Avantajele TAD

Abordarea la nivel abstract a TD ofera cƒteva avantaje importantepentru activitatea de programare Œn general: (1) specificatii precise; (2) modularitate; (3) Œncapsularea si ascunderea informatiei; (4) simplitate Œn proiectare si utilizare; (5) integritatea reprezentarii; (6) independenta de implementare si

86

Page 87: Sub Program Are

(7) modificabilitate.

Deoarece specificarea TAD se face folosind un limbaj precis, iarsemantica operatiilor nu face apel la reprezentarea domeniului,definitiile obtinute sunt clare, neambigue si independente de contextul deutilizare. Despre modularitate: putem discuta de doua tipuri de module: TAD siTVD. Este extrem de important ca definirea unui TAD (care utilizeazaoperatii sau elemente ale altor tipuri de date) sa utilizeze numaidefinitiile TAD respective, fara vreo referire la TVD ce le implementeaza.In exemplele prezentate de noi Œn 2.3.1, TAD Monede este construit cuajutorul TAD Natural si Sutime, Œnsa fara nici o referire la implementareaacestora. In mod natural, implementarea unui astfel de TAD va trebui saapeleze operatiile TAD ale componententelor sale, prevazute Œn specificare. Incapsularea reprezentarii si operatiilor (punerea lor Œmpreuna)induce ordine si eficienta. Pentru o anumita structura de date estenatural sa se conceapa nu numai o operatie, ci toate operatiile demanipulare a respectivei structuri. Acest lucru, facut independent deutilizarea respectivei structuri, permite o testare usoara a operatiilorproiectate si asigura flexibilitate, o noua operatie putƒnd fi introdusaoricƒnd, daca e nevoie. Acordarea unei atentii egale reprezentarii sioperatiilor conduce la realizarea de module mai stabile, deschizƒndpoarta reutilizarii lor Œn contexte adecvate. Ascunderea informatiei este una dintre cerintele majore alemodularizarii. Modulul client nu trebuie sa cunoasca decƒt modul de apelarea serviciilor din modulul furnizor (server). Abstractizarea datelor ofera simplitate. La prima vedere, termenulabstract ar avea Œntelesul de teoretic, abscons, greu de Œnteles. In cazulnostru, abstractizarea are o semnificatie opusa. Esenta ei consta tocmaiŒn surprinderea aspectelor si comportamentului esential al datelor(ca structura si operatii) prin neglijarea detaliilor de reprezentaresi de implementare. Aceasta concentrare pe aspectele esentiale oferasimplitate Œn proiectarea TAD de catre specialist. Pe de alta parte,cerinta de minimalitate a setului de operatii ale TAD ofera simplitate Œnutilizarea acestora, adica o Œntelegere usoara de catre utilizator acomportamentului TAD. Din aceste afirmatii nu trebuie sa se Œnteleaga caaspectele de implementare sunt neglijate. La nivelul abstract acestea nusunt luate Œn considerare nu pentru ca ele nu sunt importante, ci pentruca aceste detalii vor fi discutate la vremea lor. Principiul rafinariisuccesive (stepwise refinement) aplicat TAD Œnseamna trei etape: - abstractizare, - definirea reprezentarii si - implementare.

Un TVD poate fi vazut ca o cutie neagra, Œn care operatiile suntbutoane ce realizeaza anumite functii (operatii) ale cutiei. Proiectantul(implementatorul) cutiei cunoaste interiorul ei, dar utilizatorul(programatorul client) nu o poate desface pentru a vedea ce e Œnauntru,ci o poate folosi prin apasarea butoanelor ei. Integritatea reprezentariipoate fi discutata pe doua nivele: abstract si virtual. La nivelulabstract, integritatea TAD este asigurata de maniera Œn care suntconcepute operatiile sale. La nivelul virtual, daca toate operatiile sunt

87

Page 88: Sub Program Are

implementate corect, atunci nici una dintre ele nu poate afectaintegritatea reprezentarii. In plus, programatorul client nu va avea accesla reprezentarea informatiei din interiorul modulului server (principiulascunderii informatiei). Notiunea de integritate (consistenta) areprezentarii se poate defini cu ajutorul invariantului acesteia, careeste un predicat: I : D Ä> {false,true} .

Spunem ca reprezentarea este consistenta daca I are valoarea truepentru orice element din domeniul TAD. De exemplu, pentru TAD Monede,invariantul reprezentarii este:

I(r:Monede) = 0 <= r.Lei <= MaxLongInt si 0 <= r.Bani <= 99 .

La nivelul abstract detaliile de reprezentare si implementare aoperatiilor sunt neglijate intentionat. De asemenea, specificarea se facefolosind un limbaj specializat si nu unul de programare. Prima decizieŒn faza de implementare este alegerea LP. Acest lucru se bazeaza pecunoasterea TD si a instructiunilor cu care acest limbaj poate opera siare o importanta decisiva Œn succesul implementarii. Urmatoarea etapaimportanta este stabilirea reprezentarii TD, concretizare a celei abstracteprin utilizarea TD ale LP ales. Pentru acelasi TAD se pot alegereprezentari structural diferite, ceea ce conduce la algoritmi diferiti.Mai mult, pentru aceeasi reprezentare se pot concepe mai multi algoritmi(deci implementari diferite) pentru o aceeasi operatie. Daca specificareaoperatiei o este corecta, modificarea codului de implementare al ei areun efect local, aceasta neimplicƒnd modificari Œn alte module sau programece apeleaza o. Multi autori recomanda o abordare gradata a ultimelor douafaze de implementare a unui TAD, Œn maniera urmatoare, pentru oreprezentare fixata: (1) se stabilesc algoritmii cei mai simpli pentru fiecare operatie; (2) se implementeaza operatiile; (3) se testeaza TVD Œn ansamblu (teste de anduranta), evidentiindu-se operatiile care sunt mari consumatoare de resurse (timp sau memorie); (4) se reproiecteaza algoritmii Œn cauza si se reia ciclul de la faza (2).

Procesul se Œncheie cƒnd Œn faza (3) rezultatele obtinute satisfaccerintele impuse. In cazul cƒnd nu se obtin rezultate satisfacatoare, seŒncearca o alta reprezentare.

2.4. Exemple

In cele ce urmeaza vom ilustra implementarea Œn Turbo Pascal a TADspecificate Œn paragraful 3.2. Datorita restrictiilor limbajului, numele modulului va fi compus dinlitera U (de la Unit) si numele TAD.

2.4.1. TVD Zile

TAD Zile poate fi implementat natural Œn Turbo Pascal, folosind TVDenumerare. Remarcam parcurgerea circulara a domeniului SAP pentru

88

Page 89: Sub Program Are

operatiile Ieri si Mƒine si implementarea acestora ca subprograme de tipfunctie Œn Turbo Pascal.

Vezi UZile.PAS

2.4.2. TVD Natural

Implementarea TAD Natural pe care o prezentam corespunde numerelornaturale ce se pot reprezenta Œn memoria calculatorului pe 4 octeti(intervalul [0..MaxLongInt], MaxLongInt = 2^31-1 Œn Turbo Pascal).Un numar natural este deci considerat Œntreg masina pe 4 octeti. Tinƒndcont de aceasta reprezentare, implementarea TAD Natural se face extrem desimplu, folosindu-se operatiile tipului predefinit LongInt (aritmetice sirelationale) din Turbo Pascal. Sugeram alte doua modalitati dereprezentare a TVD Natural, diferite de cea aleasa, care sa permitamanipularea de numere naturale n > MaxLongInt, cu: - n reprezentat ca tablou cu MaxDim cifre (MaxDim constanta, elementul de tablou sa fie de tip Byte si sa contina o cifra a numarului scris Œntr-o baza de numeratie numita Baza; (elementul de indice i va contine cifra de rang i = coeficientul lui Baza^i); - n reprezentat ca lista Œnlantuita, nodul i al listei continƒnd ca informatie utila cifra i a numarului scris Œn baza Baza.

Pentru fiecare dintre cele doua reprezentari sugerate, implementareatrebuie refacuta, plecƒnd de la specificatiile 2 si obtinƒndu-se astfel noivariante ale TVD Natural, care vor diferi ca functionalitate de cea de maijos doar prin domeniu.

Vezi Unit-ul UNatural.PAS

Din motive de simplificare a implementarii, la operatiile Aduna siOri nu este testata depasirea valorii maxime reprezentabile, MaxLongInt.Sugeram cititorului sa realizeze singur o alta implementare a acestoroperatii, Œn care sa se semnaleze eroare Œn cazul depasirii.

2.4.3. TVD Sutime

Implementarea TAD Sutime pe care o prezentam considera reprezentareadomeniului acestui TAD ca subdomeniu al tipului predefinit Integer dinTurbo Pascal. Tinƒnd cont de aceasta reprezentare, implementarea se faceextrem de simplu, folosindu-se operatiile tipului predefinit Integer(aritmetice si relationale) din Turbo Pascal. In interfata si implementareaunit-ului se foloseste TVD Natural, prezentat Œn 2.4.2.

Vezi Unit-ul USutime.PAS

Analizƒnd implementarea operatiei Aduna, putem sugera si o altasolutie, mai generala:

Procedure Aduna(s1,s2:Sutime; var n:Natural; var s:Sutime); Var

89

Page 90: Sub Program Are

n1,n2,n3 : Natural; Begin n1 := SutimeToNatural(s1); n2 := SutimeToNatural(s2); UNatural.Adun_(n1,n2,n3); UNatural.Cƒt(n3,100,n); UNatural.Rest(n3,100,n1); s := NaturalToSutime(n1) End; { Aduna }

Œn care am folosit operatii de conversie de la tipul Sutime la tipul Natural(SutimeToNatural) si invers (NaturalToSutime), iar calculele s-au facutfolosind operatiile tipului Natural.

2.4.4. TVD Monede

Acest TVD este un exemplu de folosire intensiva a altor TVD (Natural,Sutime) pentru implementarea TAD Monede. Se remarca calificarea operatieicu numele modulului (Œn situatia conflictului de nume). Folosireaoperatiilor relationale ale TVD Natural (MaiMare, Egal) Œn loculoperatorilor relationali (predefiniti) este un exemplu de apel independentde implementare: daca TVD Natural ar fi altfel implementat (vezi sugestiilefacute Œn 3.2.3.1), n-ar putea fi folositi respectivii operatori, prinurmare codul sursa al TVD Monede (operatiile MaiMare si Egal) ar trebuirescris). Utilizƒnd operatiile cu care a fost dotat TAD Natural, se obtineo implementare mult mai generala si mai flexibila, care nu este afectatade modificarea TVD folosite Œn ea.

Vezi UMonede.PAS

4. PROGRAMAREA ORIENTATA PE OBIECTE

4.1. Concepte generale4.2. Programarea orientata pe obiecte in Turbo Pascal

4.1. Concepte generale

Am vazut in lectia 'Tipuri de date utilizator' ca

programarea orientata pe obiecte = programarea bazata pe obiecte + mostenirea + polimorfismul.

Inainte de a defini conceptele de mostenire si polimorfism, vom face o scurtatrecere in revista a conceptelor de baza legate de programarea orientata peobiecte (Object-Oriented Programming, OOP sau POO).

4.1.1. Notiuni si concepte fundamentale

In programarea bazata pe obiecte (Object-Based Programming), obiecteleincapsuleaza date (ce caracterizeaza starea lor) si actiuni (carecaracterizeaza comportamentul obiectelor). Obiectele comunica intre ele prin

90

Page 91: Sub Program Are

mesaje. Doua obiecte de acelasi tip (adica instante ale aceleiasi clase) voravea aceeasi multime a variabilelor de stare (numite campuri sau variabile deinstanta), dar vor avea valori diferite ale acestora, ce le va deosebi. Inschimb, din punct de vedere comportamental, ele vor fi capabile sa efectuezeaceleasi operatii (numite metode); natural, fiecare obiect va efectua oanumitt operatie in conformitate cu starea sa. Cu alte cuvinte, doua obiecteidentice (ca tip) vor reactiona diferit la acelasi mesaj, deoarece raspunsultine cont de starea fiecaruia dintre ele.

In preambulul de mai sus am folosit notiunile: - obiect, - clasa, - instanta sau realizare, - camp sau variabila de instanta sau variabila de stare, - metoda sau operatie, - mesaj.Sa le definim.

a) Clasa

Clasa este un tip de date reprezentand o multime de obiecte care au in comunaceeasi reprezentare (aceleasi variabile de stare, numite si campuri sauvariabile de instanta) si acelasi comportament (aceleasi operatii, numitemetode). Conceptual, ea poate fi considerata la doua nivele:

1. La nivelul abstract: clasa este un tip abstract de date; campurile definesc reprezentarea sa; operatiile definesc comportamentul obiectelor (instantelor) sale;

2. La nivelul virtual: - clasa este un mecanism existent intr-un limbaj de programare, ce permite: - incapsularea datelor si operatiilor; - stabilirea unor reguli de vizibilitate (acces la campuri din mediul extern ei;

mecanism de protectie a datelor); - asocierea unui nume entitatii respective (mecanism de tipizare); - posibilitatea creerii si distrugerii instantelor (realizarilor) sale (mecanism de instantiere, constructori, destructori); - campurile clasei se numesc variabile de instanta sau variabile de stare; - operatiile (serviciile) se numesc metode; multimea metodelor clasei formeaza protocolul de comunicatie al obiectelor ei

Deci, la nivelul abstract clasa este un tip abstract de date, iar in a douaacceptiune, clasa este un tip virtual de date.

b) Obiectul

Obiectul este instanta (realizare) a clasei, in ambele acceptiuni discutatemai sus. La nivelul virtual, putem considera obiectul ca o variabila de tipul

91

Page 92: Sub Program Are

clasei. Obiectele comunica intre ele prin mesaje. In forma generala, un mesajse poate scrie astfel:

send(R,S[,A])sau R.S(A)

unde: R(receptor) este obiectul destinatar al mesajului S(selector) este metoda apelata A(argumente, optional) contine parametrii actuali ai apelului

Obiectul receptor R, instanta a clasei C, poate raspunde la mesaje cuselectorul S numai daca metoda desemnata de S face parte din protocolul decomunicatie al clasei C. Altfel spus, S este numele unei metode si aceastametoda trebuie sa fie definita in clasa C.

4.1.2. Mostenirea si polimorfismul - o prima abordare

Definitiile date in acest paragraf sunt imprecise, dar constituie un punct deplecare in discutia noastra. Ele vor fi detaliate in sectiunile urmatoare.

Mostenirea permite clasificarea obiectelor in concordanta cu caracteristicilecomune (de stare sau de comportament) ale lor. Polimorfismul dinamic inseamnatrimiterea de mesaje spre obiecte de tip necunoscut, dar care recunosc mesajul(selectorul acestuia face parte din protocolul de comunicatie al clasei lor).Numim obiecte polimorfice acele obiecte care au acelasi protocol decomunicatie. Impreuna, mostenirea si polimorfismul sunt instrumente ce permitrealizarea a doua mari deziderate: - organizarea ierarhiilor de clase; - simplificarea comunicarii intre obiecte.

Cu aceste notiuni precizate, se pot formula doua (pseudo)definitii aleprogramarii orientate pe obiecte (POO): 1. Esenta POO este trimiterea de mesaje spre obiecte de tip necunoscut, care au in comun acelasi protocol de comunicatie; 2. POO este un stil de programare (mai bine zis de dezvoltare de programe) ce incearca minimizarea complexitatii unui program prin reducerea numarului de conexiuni intre componentele sale. Caile de reducere a complexitatii sunt:

- transmiterea de mesaje;- definirea unui protocol de comunicatie simplu si, in acelasi timp,

general.

Etapele de realizare a unui program orientat pe obiecte respecta in generaletapele prezentate pana acum, la programarea bazata pe obiecte. Ele sunt:

1. Crearea de clase, ce definesc reprezentarea si comportamentul obiectelor; 2. Crearea de obiecte, instante ale claselor; 3. Crearea programului, vazut ca o secventa de comunicari intre obiecte (programul fiind un sir de mesaje). Cu alte cuvinte, programul este o colectie structurata de obiecte care comunica intre ele.

92

Page 93: Sub Program Are

Aceste etape vor fi detaliate in sectiunea 4.3, succesiunea lor bazandu-se peurmatoarele observatii:

1. In proiectarea unui program este nevoie de identificarea si clasificarea obiectelor ce concura la realizarea functiunilor programului; 2. Clasificarea obiectelor impune ordine, atat pentru sistemul real (o ordine naturala, proprie acestuia), cat si pentru modelele sale (logic si fizic); 3. Obiectele se clasifica dupa caracteristicile lor comune; 4. In functie de locul de folosire a obiectelor, caracteristicile lor pot fi accentuate sau ignorate; 5. Obiectele cu aceleasi caracteristici se grupeaza in clase.

Sa consideram un produs program destinat evidentei marfurilor intr-unsupermagazin.

Tinand cont de observatiile de mai sus, primul lucru care trebuie facut esteidentificarea obiectelor. In cazul nostru, sistemul real este compus celputin din urmatoarele categorii de obiecte (fiinte, lucruri, compartimente): - marfuri, - magazii, - case de marcaj, - standuri de vanzare, - personal, - cumparatori.

Dintre toate aceste obiecte, vom discuta in continuare doar despre marfuri.In sistemul real (supermagazinul), obiectele (reale) care sunt manipulate(vandute) sunt marfuri. Prin urmare am identificat obiectele reale si urmeazasa le clasificam. Un supermagazin comercializeaza o mare diversitate demarfuri. Din multe considerente (o gestiune mai usoara, atractivitate siusurinta in gasirea marfii pentru cumparatori), spatiul comercial alsupermagazinului este impartit in raioane, a caror denumire caracterizeazagenul de marfuri comercializate. Uzual putem gasi raioanele: - alimentar, - textile, - librarie-papetarie, - tutungerie, - cosmetice, - incaltaminte, - menaj, - electrice, - electronice, - articole de lux s.a.

Iata ca sistemul real ofera o clasificare a marfurilor, facuta dupa diverseproprietati ale acestora (provenienta, utilizare). Este firesc ca aceastaclasificare naturala sa se prelungeasca si in modelele logic si fizic alesistemului real. Prin urmare, putem discuta de obiectul MARFA, careare ca descendenti obiectele ALIMENTARE, TEXTILE, LIBRARIE, TUTUNGERIE,COSMETICE, INCALTAMINTE, MENAJ, ELECTRICE, ELECTRONICE, ARTICOLE DE LUX.

93

Page 94: Sub Program Are

Caracteristicile comune ale tuturor marfurilor le vom grupa in obiectul MARFA.In analiza care trebuie facuta, trebuie considerate obiectele din diversepuncte de vedere. Pentru un cumparator sunt importante functionalitatea sipretul de vanzare. Functionalitatea este o caracteristica mai greu decuantificat, motiv pentru care ea va fi tratata distinct la fiecareclasa in parte, insa pretul este extrem de important: in supermagazinediferite, aceeasi marfa poate sa aiba preturi diferite. Din punctul de vedereal gestiunii magazinului sunt importante si alte caracteristici, ca deexemplu: - codul marfii (ce asigura o identificare unica a ei), - denumirea, - unitatea de masura, - pretul de achizitie, - pretul de transport, - adaosul comercial aplicat, - TVA, - stocul din depozit, - intrari (achizitie de marfa de la furnizori), - iesiri (vanzare de marfa).

Unele dintre aceste caracteristici vor servi la determinarea pretului devanzare.

Caracteristicile de mai sus sunt comune tuturor marfurilor. Pentru clasele demarfuri enumerate, se pot identifica si caracteristici specifice fiecareia,ca de exemplu:

ALIMENTARE : termen de valabilitate TEXTILE : tip fire INCALTAMINTE : marime ELECTRICE : putere consumata ELECTRONICE : termen de garantie ARTICOLE DE LUX : accize

Natural, fiecare din marfurile mai sus enumerate, fiind descendentiai obiectului MARFA, vor avea toate caracteristicile acestuia. Se observa dejareutilizarea specificarii: obiectul de baza contine caracteristicile comune,iar pentru descendentii acestuia se specifica numai ceea ce-i deosebeste deparinte.

In explicatiile de mai sus, am identificat si clasificat obiecte din sistemulreal, determinand caracteristicile comune sau specializate ale lor. Conformultimei reguli (5), trebuie sa grupam aceste obiecte in clase. Gruparea sepoate face numai dupa ce caracteristicile obiectelor (grupurilor de obiecte)sunt scoase in evidenta. Claselor nu le mai corespund obiecte reale; claselesunt descrieri abstracte ale obiectelor reale. Clasificarea acestor obiecte vaforma o ierarhie de clase. Pentru exemplul nostru, ierarhia clasei MARFA esteilustrata in figura 1. Natural, fiecare subclasa a clasei MARFA poate fila randul ei dezvoltata in continuare.

ÚÄÄÄÄÄ¿ cod unitate de masura stoc intrari

94

Page 95: Sub Program Are

³MARFA³ denumire pret achizitie TVA iesiri ÀÄÄÂÄÄÙ pret pret transport adaos comercial ³ ÚÄÄÄÄÄÁÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄ¿ÚÄÄÄÁÄÄÄ¿ÚÄÄÄÄÄÄÁÄÄÄÄÄ¿ÚÄÄÄÄÁÄÄÄÄ¿ÚÄÄÄÄÄÁÄÄÄÄÄ¿ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄ¿ ³ALIMENTARE³³TEXTILE³³INCALTAMINTE³³ELECTRICE³³ELECTRONICE³³ARTICOLE DE LUX³ ÀÄÄÄÄÄÄÄÄÄÄÙÀÄÄÄÄÄÄÄÙÀÄÄÄÄÄÄÄÄÄÄÄÄÙÀÄÄÄÄÄÄÄÄÄÙÀÄÄÄÄÄÄÄÄÄÄÄÙÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ termen de tip marime putere termen de accize valabi- fire consumata garantie litate

Figura 1. Ierarhia clasei MARFA

Odata clasele identificate, trebuie realizata specificarea lor, cand multedintre elementele discutate informal trebuiesc precizate. Astfel, uneledintre caracteristici sunt date, ca de exemplu denumirea, codul, unitatea demasura; altele, cum este pretul (de vanzare) se pot calcula (pe baza unuialgoritm de calcul). Primele sunt ceea ce am numit campuri sau variabile deinstanta, iar ultimele sunt metode ale claselor respective. Cand am facutidentificarea caracteristicilor claselor nu am precizat daca ele sunt campurisau metode. Specificarea unei clase va cuprinde, intr-o prima aproximatienumele clasei, specificarea campurilor si specificarea metodelor. Schemagenerala de specificare a unei clase este data in figura 2.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Clasa ³ ³ nume, identificator ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Campuri ³ ³ Specificare camp ³ ³ specificare camp_1 ³ ³ nume si explicatie ³ ³ specificare camp_2 ³ ³ tip de date ³ ³ ... ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ specificare camp_n ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Metode ³ ³ Specificare metoda ³ ³ specificare metoda_1 ³ ³ nume si explicatie ³ ³ specificare metoda_2 ³ ³ parametri formali ³ ³ ... ³ ³ algoritm ³ ³ specificare metoda_m ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Figura 2. Schema de specificare a clasei

Pentru clasa MARFA, schema de specificare este prezentata in figura 3.Din ratiuni de spatiu, numele unor caracteristici este trunchiat.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Clasa ³ ³ Marfa ³

95

Page 96: Sub Program Are

³ Campuri ³ ³ Cod codul marfii, numar intreg ³ ³ Denumire denumirea marfii, sir de caractere ³ ³ UM unitatea de masura, sir de caractere ³ ³ Pret_ach pretul de achizitie, numar real ³ ³ TVA procent TVA, numar real ³ ³ Adaos_c procent adaos comercial, numar real ³ ³ Stoc cantitatea existenta in magazin, ³ ³ numar intreg (real) ³ ³ Metode ³ ³ Pret_tr pret transport ³ ³ Pret_tr := 0.1 * Pret_ach ³ ³ Pret_v pret vanzare ³ ³ Pret_v := Pret_ach (1+(TVA+Adaos_c)/100) ³ ³ + Pret_tr ³ ³ Intrare(c) intrarea unei cantitati c din marfa ³ ³ Stoc := Stoc + c ³ ³ Iesire(c) vanzarea unei cantitati c de marfa ³ ³ Stoc := Stoc - c ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Figura 3. Schema de specificare a clasei MARFA

Revazad schema de specificare a unui tip abstract de date, prezentata intr-olectie anterioara putem constata ca specificarea claselor respecta aceastaschema. Din motive de spatiu, in specificarile de mai sus nu am detaliatpartea aferenta operatiilor TAD (numite aici metode). In plus fata despecificarea TAD, dupa cum se va vedea in paragraful urmator, specificareaclasei contine si alte elemente.

4.1.3. Mostenirea

Mostenirea permite definirea de noi clase, pe baza celor existente (ultimelesunt numite clase de baza sau clase parinti sau superclase). Noile claseastfel obtinute se numesc clase derivate, subclase sau clase descendente aleclaselor de baza, ele fiind specializari ale acestora. Ca regula generala, oclasa derivata mosteneste toate caracteristicile (starea si comportamentul)clasei de baza, iar specializarea (diferentele in stare sau comportament fatade clasa de baza) poate insemna: - ignorarea unor caracteristici ale clasei de baza (mai rar intalnita); - adaugarea unor noi caracteristici (variabile de stare sau metode), numita specializare prin imbogatire; - modificarea unor caracteristici ale clasei de baza (in general modificarea unor metode), adica specializarea prin inlocuire.

Prin urmare, mostenirea permite ca in clasa derivata se se specifice doarcaracteristicile noi. Ca regula generala, definitia unei clase derivatecontine: precizarea parintelui (parintilor) si precizarea noilorcaracteristici. Acest lucru ofera doua prime avantaje: - reutilizarea definitiilor si a codului: caracteristicile mostenite de la parinti nu mai trebuie nici specificate, nici codificate;

96

Page 97: Sub Program Are

- definitii mai simple ale claselor derivate (se specifica mai putine elemente), deci o mai buna intelegere a specificarii si implementarii.

Exista doua tipuri de mostenire: simpla si multipla. Mostenirea simplacorespunde unei singure clase parinte, iar mostenirea multipla presupune celputin doi parinti.

Relatia de mostenire este o legatura intre clase, de la fiu la parinte, cestabileste o relatie (partiala) de ordine pe multimea claselor. In cazulmostenirii simple, ierarhia claselor se poate reprezenta ca un arbore(arborele de mostenire, un nod corespunzand unei clase), ce are ca radacinaclasa de baza a ierarhiei, cea mai generala clasa (ce concentreazacaracteristicile comune ale tuturor claselor din ierarhie). In cazulmostenirii multiple, ierarhia claselor se reprezinta sub forma unui graf(graful de mostenire). In arborele (respectiv graful) de mostenire se potconsidera subarbori (subgrafe), in care radacina este clasa de baza asubarborelui (nodul initial al subgrafului). In cazul mostenirii simple,legatura parinte-fiu este de tipul unul_la_mai multe (unui p_rinte iicorespund mai multi descendenti), iar in cazul mostenirii multiple, legaturaeste de tipul mai_multe_la_mai multe (unui parinte ii corespund mai multidescendenti, iar un descendent poate avea mai multi parinti).

In ierarhia de mostenire, o clasa aflata intr-un nod intern sau terminal aredoua tipuri de caracteristici: - caracteristici mostenite de la parinti (clasele aflate in aval de ea in ierarhie); - caracteristici proprii, specificate in definitia ei (fie caracteristici noi, fie redefiniri ale caracteristicilor parintilor).

Reluand exemplele discutate in paragraful anterior, trebuie sa precizam caschema de specificare a unei clase trebuie sa contina, pe langa elementelepreluate de la specificarea TAD (nume, campuri si metode), si precizareaparintilor (superclaselor) clasei in cauza. Prin urmare, o schema completa despecificare a unei clase va avea structura prezentata in figura 4.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Clasa ³³ nume ³³ Superclasa ³³ lista de superclase ³³ Campuri ³³ specificarea campurilor ³³ Metode ³³ specificarea metodelor ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Figura 4. Schema completa de specificare a unei clase

Fiecare dintre clasele specificate constituie o specializare a superclaseisale, avand fie caracteristici noi (Putere_abs, Garan_ie, Accize, Tip_fire,Capacitate, Standard_VCR, Tara_furnizoare, Provenienta, Tip_ingrediente),fie redefinind caracteristici (metode) ale superclaselor (Pret_tr, Pret_v).

97

Page 98: Sub Program Are

In figura 1 este prezentat arborele de mostenire cu radacina clasa MARFA.Evident, avem de-a face in acest exemplu cu o mostenire simpla, totidescendentii avand o singura superclasa, MARFA. Ramanand in acelasi context,se pot da exemple mai elaborate de ierarhii de mostenire.

La inceput vom exemplifica mostenirea simpla, considerand trei descendenti aiclasei MARFA: ELECTRICE, ARTICOL_DE_LUX si ALIMENTE, care vor avea la randullor descendentii FRIGIDER, VIDEO si HOMAR, respectiv OUA si BISCUITI.Schemele de specificare ale acestor clase sunt date in continuare, in figura5, iar arborele de mostenire in figura 6.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Clasa ³ ³ Clasa ³ ³ Clasa ³³ ELECTRICE ³ ³ ARTICOL_DE_LUX ³ ³ ALIMENTE ³³ Superclasa ³ ³ Superclasa ³ ³ Superclasa ³³ MARFA ³ ³ MARFA ³ ³ MARFA ³³ Campuri ³ ³ Campuri ³ ³ Campuri ³³ Putere_abs ³ ³ Accize ³ ³ Tip_fire ³³ Metode ³ ³ Metode ³ ³ Metode ³³ Consum ³ ³ Pret_v ³ ³ ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Clasa ³ ³ Clasa ³ ³ Clasa ³³ FRIGIDER ³ ³ VIDEO ³ ³ HOMAR ³³ Superclasa ³ ³ Superclasa ³ ³ Superclasa ³³ ELECTRICE ³ ³ ARTICOL_DE_LUX ³ ³ ARTICOL_DE_LUX ³³ Campuri ³ ³ Campuri ³ ³ Campuri ³³ Capacitate ³ ³ Standard_VCR ³ ³ Tara_furnizoare ³³ Metode ³ ³ Metode ³ ³ Metode ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Clasa ³ ³ Clasa ³³ OUA ³ ³ BISCUITI ³³ Superclasa ³ ³ Superclasa ³³ ALIMENTE ³ ³ ALIMENTE ³³ Campuri ³ ³ Campuri ³³ Provenienta ³ ³ Tip_ingrediente ³³ Metode ³ ³ Metode ³³ Pret_tr ³ ³ ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Figura 5. Specificari de clase

ÚÄÄÄÄÄÄÄ¿ ³ MARFA ³ ÀÄÄÄÂÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-¿ ÚÄÄÄÄÄÁÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄ¿

98

Page 99: Sub Program Are

³ ELECTRICE ³ ³ ARTICOL_DE_LUX ³ ³ ALIMENTE ³ ÀÄÄÄÄÄÂÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÂÄÄÄÄÙ ³ ÚÄÄÄÄÁÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄ-¿ ÚÄÄÄÄÄÁÄÄÄÄ¿ ÚÄÄÄÄÁÄÄ¿ ÚÄÄÄÁÄÄÄ¿ ÚÄÄÁÄÄ¿ ÚÄÄÄÄÁÄÄÄÄÄ¿ ³ FRIGIDER ³ ³ VIDEO ³ ³ HOMAR ³ ³ OUA ³ ³ BISCUITI ³ ÀÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÙ

Figura 6. Arbore de mostenire

Analizand arborele de mostenire din figura 6, remarcam simplitatea lui (faptpozitiv) pe de o parte, dar si rigiditatea ierarhiei (fapt negativ), pe dealta parte. Ar parea normal ca VIDEO sa fie si descendent al clasei ELECTRICE,pentru ca func_ioneaza cu curent electric, insa acest lucru nu este posibilin mostenirea simpla.

Pentru a exemplifica mostenirea multipla, ramanem in acelasi context,considerand in plus clasele PERISABILE, FRAGILE si ALTERABILE si rescriindspecificatiile claselor VIDEO, HOMAR si OUA. Ierarhia de clase astfelobtinuta este mai complexa, insa relatiile dintre clase sunt acum mainaturale. Am putut astfel sa consideram ca VIDEO are atat caracteristicileunui aparat electric (ELECTRICE), cat si cele ale unui ARTICOL_DE_LUX. Inplus, cele trei noi clase introduse asigura o mai buna ierarhizare amarfurilor: PERISABILE (marfuri care trebuie transportate cu mijloace detransport adecvate, deci necesita conditii speciale de manipulare sitransport), FRAGILE (marfuri ce trebuie manipulate si transportate cu atentie,ca sa nu se sparga) si ALTERABILE (care se depreciaza rapid in timp),descendenta a clasei PERISABILE. In toate aceste situatii, pretul detransport va include cheltuieli suplimentare (datorate fie mijloacelorspeciale de transport, fie restrictiilor de timp), deci in specificareaclaselor metoda cu numele Pret_tr va fi redefinita. Noile specificatii suntdate in figura 7, iar graful de mostenire in figura 8.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Clasa ³ ³ Clasa ³ ³ Clasa ³³ PERISABILE ³ ³ FRAGILE ³ ³ ALTERABILE ³³ Superclasa ³ ³ Superclasa ³ ³ Superclasa ³³ MARFA ³ ³ MARFA ³ ³ MARFA ³³ Campuri ³ ³ Campuri ³ ³ PERISABILE ³³ Temperatura ³ ³ ³ ³ Campuri ³³ Metode ³ ³ Metode ³ ³ Data_expir ³³ Pret_tr ³ ³ Pret_tr ³ ³ ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ Clasa ³ ³ Clasa ³ ³ Clasa ³³ OUA ³ ³ VIDEO ³ ³ HOMAR ³³ Superclasa ³ ³ Superclasa ³ ³ Superclasa ³³ ALIMENTE ³ ³ ARTICOL_DE_LUX ³ ³ ARTICOL_DE_LUX ³³ ALTERABILE ³ ³ ELECTRICE ³ ³ PERISABILE ³³ FRAGILE ³ ³ FRAGILE ³ ³ Câmpuri ³³ Campuri ³ ³ Campuri ³ ³ Tara_furnizoare ³³ Provenienta ³ ³ Standard_VCR ³ ³ Metode ³

99

Page 100: Sub Program Are

³ Metode ³ ³ Metode ³ ³ ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Figura 7. Specificari de clase in cazul mostenirii multiple

ÚÄÄÄÄÄÄÄ¿ ³ MARFA ³ ÀÄÄÄÂÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-¿ÚÄÄÄÄÄÁÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄ¿³ ELECTRICE ³ ³ ARTICOL_DE_LUX ³ ³ FRAGILE ³ ³ PERISABILE ³ ³ ALIMENTE ³ÀÄÄÄÂÄÄÄÂÄÄÄÙ ÀÄÄÄÄÄÂÄÄÄÄÄÄÄÄÂÄÙ ÀÄÄÄÂÄÄÄÄÄÂÙ ÀÄÄÂÄÄÄÄÄÂÄÄÄÙ ÀÄÄÂÄÄÄÄÂÄÄÙ ³ ³ ³ ³ ³ ³ ÚÄÄÄÄÙ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ÚÄÄÄÄÄÄÄÄÁÄÄÄ¿ ³ ³ ³ ³ ³ ÚÄÄÄÄÄÄÅÄÄÄÄÄÄÄÙ ³ ³ ³ ALTERABILE ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ ÚÄÄÄÄÄÄÄÄÄÅÄÙ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ÚÄÄÄÄÄÄÄÙ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ÚÄÄÁÄÄÄÄÄÄÄ¿ ÚÄÁÄÁÄÁÄ¿ ÚÁÄÄÄÁÄÄ¿ ÚÁÄÁÄÁ¿ ÚÄÄÄÄÄÄÄÁÄÄ¿ ³ FRIGIDER ³ ³ VIDEO ³ ³ HOMAR ³ ³ OUA ³ ³ BISCUITI ³ ÀÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÙ

Figura 8. Graf de mostenire

Conform noilor specificatii, clasele ALTERABILE, VIDEO, HOMAR si OUA au maimulte superclase directe, prin urmare ierarhia de mostenire se reprezinta subforma unui graf.

Fie C o class si A o caracteristica a ei. Daca A este mostenita, se puneproblema determinarii superclasei SC a lui C pentru care A este caracteristicaproprie (SC va fi clasa de la care C mosteneste caracteristica A). In cazulmostenirii simple, exista un singur drum de la radacina arborelui de mostenirela nodul corespunzator clasei C, relatia de mostenire inducand o relatie deordine pe multimea claselor existente in nodurile acestui drum; identificareasuperclasei SC se face prin parcurgerea respectivului drum in sens invers (dela nodul lui C spre radacina) si inspectarea fiecarui nod intalnit. Primulnod in care se gaseste definitia caracteristicii A va corespunde clasei SC.In cazul mostenirii multiple, exista mai multe drumuri (in cazul general) dela nodul clasei C la nodul initial al grafului de mostenire. Pot exista douasituatii: - caracteristica A apartine unui singur drum (C mosteneste caracteristica A de la un singur parinte): determinarea clasei SC se face pe acest drum, la fel ca in cazul mostenirii simple; - caracteristica A apartine la cel putin doua drumuri (C mosteneste caracteristica A de la cel putin doi parinti), situatie numita conflict de mostenire; in acest caz trebuie (pe baza unei informatii suplimentare) precizat parintele de la care C mosteneste caracteristica A; intre

100

Page 101: Sub Program Are

modalitatile de rezolvare a conflictului de mostenire amintim: - stabilirea unei ierarhii intre parinti (aceasta ierarhie va dicta ordinea in care se iau in considerare drumurile); - conflictele sunt de fapt conflicte de nume; se poate incerca o schimbare a numelor sau o calificare a lor cu numele clasei parinte (neelegant); - mostenirea se specifica explicit, in genul:

from SC inherits A.(dela SC mosteneste A).

Revazand exemplul nostru, un asemenea conflict de mostenire apare in clasa OUA,pentru care metoda Pret_tr este mostenita atat de la clasa FRAGILE, cat si dela clasa PERISABILE. In astfel de situatii, in schema de specificare a claseitrebuie precizat parintele de la care se mosteneste respectiva caracteristica.O situatie analoaga poate sa apara si pentru campuri.

Relatia de instantiere este o legatura intre obiecte si clase: obiectul esteo instanta a unei singure clase. Prin urmare, aceasta legatura este de tipulunu_la_unu (de la obiect la clasa). Invers, o clasa poate avea mai multeinstante. Intr-o ierarhie de clase, nu neaparat toate clasele pot aveainstante. Exista doua categorii de clase: - clasele abstracte, ce nu genereaza instante; de obicei ele sunt in partea superioara a ierarhiei (clase de baza), continand caracteristicile comune ale tuturor claselor descendente; - clasele generatoare de instante, ce se afla in nodurile interioare sau terminale ale ierarhiei.

Revazand ierarhiile de mostenire ale clasei MARFA prezentate in figurile 6 si8, putem conchide ca MARFA este o clasa abstracta (in cazul mostenirii simple),respectiv ca MARFA, FRAGILE, PERISABILE si ALTERABILE sunt clase abstracte(pentru mostenirea multipla). Clasele ELECTRICE, ARTICOL_DE_LUX si ALIMENTEse pot considera fie abstracte, fie generatoare de instante, pe cand FRIGIDER,VIDEO, HOMAR, OUA, BISCUITI sunt doar clase generatoare de instante.

Intre clasele abstracte, un loc aparte il ocup_ clasele generice sau claseleparametrizate. Daca mostenirea permite rafinarea caracteristicilor uneimultimi de obiecte, plecand de la cele comune si specializand, obtinandu-seo ierarhie in care fiecare clasa are cel putin o caracteristica diferit inraport cu celelalte, genericitatea inseamna acelasi comportament pentru clasediferite. De exemplu, o stiva de intregi, o stiva de siruri de caractere sauo stiva de inregistrari PERSOANA vor avea (toate) comportamentul generic alstivei (operatiile Push, Pop si Top), dictat de disciplina de servire LIFO(Last In First Out). Ceea ce difera la cele trei exemple de stive este tipulelementului supus manipularii: intreg, sir de caractere, inregistrare de tipPERSOANA. Genericitatea (in acele limbaje in care este implementata) permiteparametrizarea claselor. Pentru exemplul nostru, va fi suficienta declarareaunei clase Stiva[Type] unde Type este tipul generic al elementului stivei.Utilizarea acestei clase generice in cazurile particulare enumerate inseamnainstantierea acestei clase generice:

StivaInt = new Stiva[Integer];StivaStr = new Stiva[String];

101

Page 102: Sub Program Are

StivaPER = new Stiva[PERSOANA];

Instantele unei clase generice sunt vazute diferit in limbaje diferite. Spreexemplu, in Ada (clasa=pachet), instanta unui pachet generic este tot unpachet, generator de instante terminale (obiecte). Ca si in cazul mostenirii,genericitatea are ca efect reutilizarea codului scris.

4.1.4. Polimorfismul

Intr-un program pot exista obiecte diferite, care sa fie instante ale unorclase legate intre ele prin relatia de mostenire. Le numim obiecte inrudite(polimorfice) deoarece: - ierarhia claselor ale caror instante sunt are o clasa radacina, clasa de baza; - clasa de baza defineste protocolul de comunicatie comun tuturor obiectelor inrudite: toate obiectele sunt capabile sa raspunda la aceleasi mesaje (cum raspund e alta problema, important este ca inteleg aceleasi mesaje); - sunt de tipuri (instante de clase) diferite.

Stricto sensu, polimorfism inseamna mai multe forme (aspecte, infatisari).Termenul este imprumutat din biologie, unde se defineste astfel: variatie informa si functiile membrilor unor specii cu stramosi comuni in arborele deevolutie (diversitate morfofiziologica in acelasi plan sau in planuridiferite de structura). In terminologia POO, prin polimorfism intelegemabilitatea de a: 1. pune obiecte inrudite intr-un tablou sau colectie; 2. utiliza protocolul de comunicatie pentru a transmite mesaje obiectelor individuale, printr-o referire unitara (ca elemente de tablou sau colectie).

In primul exemplu, toate marfurile ce se comercializeaza in supermagazinformeaza o asemenea colectie de obiecte polimorfice. Ele au in comun acelasiprotocol de comunicatie (definit in clasa radacina a ierarhiei de mostenire,MARFA), pe de o parte, fiind insa instante ale unor clase diferite, pe dealta parte. Spre exemplu, orice marfa se manipuleaza (se achizitioneaza dela furnizori si se vinde), deci asupra ei se executa operatii ca Intrare siIesire si are un pret (determinat cu metoda Pret_v). Pentru operatiileIntrare si Iesire, metoda de calcul este descrisa in clasa MARFA (Figura 3),ea fiind general valabila in cazul oricarei marfi. Nu acelasi lucru seintampla in cazul metodei Pret_v, care este redefinita in cazul claseiARTICOL_DE_LUX, deoarece pentru aceste articole se aplica taxe fiscale maimari, numite accize. Un exemplu de algoritm pentru pretul de vanzare alarticolelor de lux ar putea fi:

ARTICOL_DE_LUX.Pret_v := MARFA.Pret_v + Pret_ach * Accize / 100

Prin urmare, daca avem o marfa M si dorim sa aflam pretul ei de vanzare,pentru acest exemplu simplificat conventia de apelare ar putea fi:

M.Pret_v

unde Pret_v are semnificatia ARTICOL_DE_LUX.Pret_v daca M este articol de lux,

102

Page 103: Sub Program Are

respectiv MARFA.Pret_v cand M este o alta marfa (M va mosteni Pret_v de laclasa de baza MARFA). Se remarca folosirea aceleiasi notatii, decisimplificarea scrierii. Acesta este un prim exemplu (mai simplu) depolimorfism, realizat numai cu ajutorul mostenirii.

O situatie analoaga este oferita de metoda Pret_tr, care este redefinita inclasa OUA (in cazul mostenirii simple), respectiv in clasele PERISABILE siFRAGILE (la mostenirea multipla). Ce se intampla insa cand o metoda (cum estePret_tr) redefinita intr-o clasa fiu trebuie folosita in clasa de baza? Spreexemplu, revazand algoritmul de calcul al pretului de vanzare pentru clasaMARFA (figura 3),

MARFA.Pret_v := MARFA.Pret_ach + (1 + (MARFA.TVA + MARFA.Adaos_c) / 100) + MARFA.Pret_tr

ne punem intrebarea: cum se va calcula OUA.Pret_v? Raspunsul nu este asa desimplu cum pare la prima vedere. Elementele de calcul ale pretului trebuiesa fie caracteristici (campuri sau metode) ale clasei OUA, deci

OUA.Pret_v := OUA.Pret_ach + (1 + (OUA.TVA + OUA.Adaos_c) / 100) + OUA.Pret_tr

Dintre acestea, doar Pret_tr este metoda proprie clasei OUA (fiind redefinitain specificarea 5, la mostenirea simpla, respectiv mostenita de la FRAGILEsau PERISABILE in specificarea 7, la mostenirea multipla). Toate celelalteelemente de calcul sunt proprii clasei MARFA, prin urmare pretul ar trebuisa se calculeze astfel:

OUA.Pret_v := MARFA.Pret_ach + (1 + (MARFA.TVA + MARFA.Adaos_c) / 100) + OUA.Pret_tr

Suntem deci in situatia cand, pentru a se obtine un rezultat corect, o metodadefinita in clasa de baza trebuie sa apeleze o metoda dintr-o clasa derivata.Aceasta este a doua fata a polimorfismului, mult mai atractiva, dar mai greude realizat. Din pacate, numai mostenirea nu este suficienta pentru a punein practica acest lucru, deoarece, asa cum am aratat in 4.1.3, relatia demostenire este o legatura de la fiu la parinte, deci de la clasa derivata laclasa de baza. Definitia metodei Pret_v fiind facuta in clasa de baza, MARFA,folosind (numai) relatia de mostenire se va ajunge la o definitie de forma:

OUA.Pret_v := MARFA.Pret_ach + (1 + (MARFA.TVA + MARFA.Adaos_c) / 100) + MARFA.Pret_tr

astfel: 1) pentru OUA.Pret_v se deduce (din arborele sau graful de mostenire) ca MARFA este clasa in care Pret_v este metoda proprie; 2) Pentru elementele de calcul (Pret_ach, TVA, Adaos_c, Pret_tr) se deduce ca toate sunt proprii clasei MARFA; daca n-ar fi asa, ele ar fi cautate

103

Page 104: Sub Program Are

in ierarhia de mostenire, in superclasele clasei MARFA (daca ar exista asemenea superclase) si nicidecum inapoi, in subclase.

In concluzie, putem rezuma caracteristicile polimorfismului astfel:

1. Polimorfismul necesita mostenire. Fara mostenire nu am avea o clasa de baza, deci nu ar exista protocolul comun de comunicatie; 2. Mostenirea nu este suficienta pentru realizarea polimorfismului, fiind nevoie de mecanisme suplimentare. In cele ce urmeaza vom discuta modul in care interactioneaza aceste doua mecanisme si de ce mecanisme noi mai e nevoie; 3. Polimorfismul simplifica munca programatorului, uniformizand sintaxa mesajelor si micsorand complexitatea programelor.

Diferentele dintre polimorfism si mostenire le putem discuta in raport cu treiaspecte: scop, arie de cuprindere si efecte. Mostenirea are ca scopierarhizarea claselor (tipurilor de date), in ideea unei mai bune structuraria universului obiectelor, prin eliminarea redundantelor, iar polimorfismulsimplifica comunicarea cu sau intre obiectele inrudite. Din punctul de vedereal ariei de cuprindere, mostenirea implica toate caracteristicile claselor(campuri si metode), pe cand polimorfismul are ca obiect doar metodele cedefinesc protocolul de comunicatie (de fapt numai metodele virtuale, definitein cele ce urmeaza, vezi 4.1.5). Mostenirea are ca efect reutilizarea coduluisi permite manifestarea polimorfismului; polimorfismul utilizeaza mostenireapentru a construi ierarhii de tipuri polimorfice, ce au in comun acelasiprotocol de comunicatie, definit in clasa de baza.

4.1.5. Consideratii de specificare si implementare a POO

4.1.5.1. Clasa ca tip abstract de date

In sec_iunile anterioare, am asimilat o clasa de obiecte cu un TAD. Fata deschema generala de specificare a unui TAD, trebuie considerate noi aspecte,legate de: - specificarea mostenirii; - regulile noi de vizibilitate si acces induse de mostenire; - specificarea operatiilor (metodelor).

Specificarea mostenirii apare la definirea unei subclase, cand trebuiescfacute precizari privind: - parintele (in mostenirea simpla); - parintii si ierarhizarea lor (in mostenirea multipla).

Regulile de vizibilitate si drepturile de acces s-au rafinat. In functie devizibilitatea lor in exterior, caracteristicile unei clase se grupeaza in(exemplu: limbajul C++): - publice (se pot accesa si/sau modifica in exterior); - private (nu se pot accesa si/sau modifica in exterior); - protejate (se pot accesa si/sau modifica doar in subclase).

In plus, la specificarea mostenirii se pot prevedea modificatori de acces aicaracteristicilor claselor parinti:

104

Page 105: Sub Program Are

- public: pastreaza aceleasi drepturi de acces pentru descendenti la caracteristicile mostenite; - privat: interzice accesul pentru descendenti la caracteristicile mostenite.

In sfarsit, o alta modalitate de acces este oferita de clasele prietene(friend in limba engleza). Daca o clasa C este prietena a unei clase C',atunci C va avea acces la campurile private ale clasei C'.

La TAD am discutat tipurile de operatii dupa functionalitatea lor. In cazulclaselor din POO, unele operatii capata noi valente (constructorii sidestructorii) si apare o alta clasificare a operatiilor, dupa criteriulmomentului legarii (metode statice si virtuale). In paragraful urmator vomdetalia noile aspecte mentionate.

4.1.5.2. Clasa in POO

Constructori si destructori

In POO, constructorii sunt specifici claselor. In afara functiilor cunoscutedeja de la programarea bazata pe obiecte (creare de obiecte, cu initializareaunor variabile de stare), in POO constructorii au si o alta functie: pun"semnatura" clasei in obiect. Practic, ne putem imagina acest lucru in felulurmator: un obiect O al unei clase C va fi o variabila ce va contine: - spatiu pentru variabilele de stare (in conformitate cu definitia clasei C); - o legatura spre clasa C (un pointer, de exemplu), ce reprezinta materializarea relatiei de instantiere.

Avem o prima regula:

R1. Orice obiect O al unei clase C se construieste prin apelul unui constructor.

(Daca regula R1 ar fi incalcata, ar putea exista obiecte care sa nu-si recunoasca prototipul, adica clasa ale carei instante sunt).

Informatia de instantiere este utila la executie, cand cu ajutorul ei sepoate determina exact clasa instantei (obiectului). Aceasta regula estevalabila in cazul programarii orientate pe obiecte. In programarea bazata peobiecte nu este necesara existenta constructorilor in acceptiunea de mai sus.

Analog, un destructor (in afara functiilor deja cunoscute) trebuie sadistruga si informatia de instantiere.

Legare statica si dinamica

Limbajele de programare folosesc din plin notiunea de identificator. Prinidentificatori, programatorul face notatii (intre altele) pentru variabile sisubprograme. Un identificator poate fi vazut in doua momente distincte:declararea si referirea. In general, o declarare a unei variabile se facefolosind un tip de date cunoscut, iar declararea unui subprogram

105

Page 106: Sub Program Are

respecta cerintele sintactice ale fiecarui limbaj in parte: se specifica deobicei tipul subprogramului (procedura sau functie, la ultima si tipulrezultatului obtinut) si lista parametrilor formali (nume si tip). Incepandcu abstractizarea datelor, putem vorbi atat de declararea unui subprogram(vazut ca o operatie a unui TAD), cat si de definirea acestuia.Declararea se face in partea publica a modulului (interfata acestuia) si inunele limbaje nu specifica decat tipul parametrilor; definirea precizeazacodul, ea fiind proprie implementarii modulului.

Prin legare intelegem o operatie ce are loc intr-un program traducator(compilator sau interpretor), ce consta in inlocuirea referirii unuiidentificator (nume de variabila sau de procedura ce apare in textul sursa)printr-o adresa (din codul programului, rezultat in urma traducerii). Incazul compilatoarelor, fazele de compilare si executie ale aceluiasi programse desfasoara la momente diferite de timp (intai compilarea integrala atextului sursa si apoi executia codului rezultat in urma compilarii). Inschimb, in interpretoare aceste doua faze se desfasoara principial una dupaalta pentru fiecare linie sursa (intai traducere, apoi executie).

In limbajele de programare dotate cu compilatoare, spunem ca legarea estestatica, in sensul ca, intr-o faza oarecare a traducerii, compilatorul poateasocia referirii unui nume o adresa: adresa unei variabile (cand avem un numede variabila) sau adresa punctului de intrare a unui subprogram (cand avemun apel de procedura sau de functie). Cum de obicei declararea unuiidentificator precede referirea lui, declararea are ca efect fie alocarea despatiu in cod (pentru variabile), fie generarea de cod (pentru subprograme),in ambele cazuri memorandu-se (sa zicem ca in tabela de simboluri)corespondenta nume-adresa (de variabila sau de punct de intrare). Si ca safim si mai exacti, ceea ce nu poate rezolva compilatorul (in cazul compilariiseparate a programelor) va rezolva editorul de legaturi, principiul ramanandacelasi.

In cazul limbajelor puternic tipizate, in momentul legarii se mai fac si alteverificari de compatibilitate: - a tipurilor (pentru variabile); - a listelor de parametri actuali (pentru apelurile de subprograme).

In limbajele de programare dotate cu interpretoare, spunem ca legarea estedinamic, ea avand loc in momentul executiei.

Fiecare dintre tipurile de legare discutate prezinta avantaje si dezavantaje,pe care nu le discutam aici.

Implementarea polimorfismului

Am vazut ca polimorfismul presupune tratarea unitara a obiectelor inrudite,ce au in comun acelasi protocol de comunicatie, definit de clasa de baza. Infapt, protocolul de comunicatie inseamna o lista de identificatori (nume demetode definite in clasa de baza). Am vazut de asemenea ca obiectele inruditenu au comportament identic, deci un acelasi identificator de metoda poate sarefere metode semantic diferite (deci actiuni diferite) pentru doua obiectedin clase diferite.

106

Page 107: Sub Program Are

Manipularea obiectelor polimorfice inseamna parcurgerea etapelor: 1. Se declara o colectie (lista, tablou) de obiecte in care un element al colectiei are tipul clasei de baza; 2. Se creeaza si se introduc in colectie obiecte de tipuri descendente ale clasei de baza (colectia va fi heterogena); crearea de obiecte se va face apeland constructorii proprii claselor respective, care vor pune semnatura clasei in instante; 3. Esenta polimorfismului este ca toate elementele colectiei se pot trata unitar in maniera:

send(EC,S[,A]) sau EC.S(A)

unde: EC (receptorul) este un element al colectiei; S (selectorul, numele metodei) apartine protocolului de comunicatie definit de clasa de baza.

Consideram ca discutam despre un limbaj dotat cu compilator. Se puneintrebarea:

Se poate folosi legarea statica pentru implementarea polimorfismului?

Raspunsul este UN NU CATEGORIC! Sa vedem de ce.

1. La compilare nu se cunoaste exact clasa receptorului EC, ci doar clasa de baza a lui, pe baza instructiunii de declarare a colectiei. Ar fi bine doar daca clasa EC coincide cu clasa de baza; 2. Mesajul de mai sus se traduce printr-un apel al metodei S; la compilare se va alege in toate cazurile (indiferent carei clase ar apartine EC) metoda S a clasei de baza. Din nou ar fi bine doar daca clasa EC coincide cu clasa de baza sau daca metoda corespunzatoare clasei EC ar fi mostenita din clasa de baza; 3. Ca sa se aleaga metoda adecvata clasei lui EC, ar trebui sa se cunoasca clasa lui EC, deci compilatorul ar trebui sa aiba acces la reprezentarea obiectului EC (unde se gaseste si "semnatura" clasei, pusa de constructor). Indiferent daca EC este un obiect static sau dinamic, el va exista numai dupa apelul constructorului, care apel inseamna o actiune efectuata la executia programului, si nu la compilare. Chiar daca la compilare avem acces la reprezentarea obiectului (cand acesta este alocat in segmentul de date; n-am avea acces daca este alocat in stiva sau in heap), informatia de instantiere nu este completata, deci nu se poate deduce clasa obiectului; 4. Cum se poate face distinctie intre metode care se leaga static si cele care se leaga dinamic? La aceasta intrebare nu putem inca raspunde, dar trebuie sa avem in vedere o atare situatie.

Din discutia de mai sus rezulta ca (pentru compilatoare): - polimorfismul se poate implementa numai prin legare dinamica; - protocolul de comunicatie trebuie sa precizeze si tipul legarii (legare

107

Page 108: Sub Program Are

statica sau legare dinamica).

Metode statice si virtuale

Am vazut ca pentru o colectie de obiecte polimorfice, clasa de baza definesteprotocolul de comunicatie. In acelasi timp, am constatat ca nu este suficientadefinirea metodelor comune, compilatorul trebuind sa cunoasca si tipullegarii pentru fiecare dintre metodele ce formeaza protocolul de comunicatie(celelalte metode se vor lega static).

Acesta este ultimul impediment in implementarea polimorfismului si el serezolva prin asa-numitele metode virtuale. Termenul de procedura virtualaeste introdus pentru prima data in Simula67, fiind preluat intre altele deC++ (functie virtuala) si Turbo Pascal (metoda virtuala).

Avem, prin urmare, inca un criteriu de clasificare a metodelor unei clase,dupa momentul legarii lor: - metode statice (legare statica, la compilare); - metode virtuale (legare dinamica, la executie).Cu aceste precizari, putem defini mai exact protocolul de comunicatie, cafiind format din metodele virtuale ale clasei de baza.

Proiectarea protocolului de comunicatie trebuie sa respecte regulile:

R2. Constructorii nu pot fi metode virtuale; (ei sunt responsabili cu "identitatea" obiectelor, deci trebuie legati static); R3. Destructorii pot fi metode virtuale; R4. Daca o metoda V este declarata virtuala intr-o clasa C, toate redefinirile ei din descendentii lui C vor fi virtuali; R5. Specificarea metodelor virtuale proprii trebuie sa corespunda celei din clasa de baza, pentru orice clasa descendenta a clasei de baza; (optional, la limbajele puternic tipizate, cand compilatorul verifica lista parametrilor actuali) R6. O clasa ce are metode virtuale trebuie sa posede cel putin un constructor.

Obiectele la munca

Triada constructor - metoda virtuala - legare dinamica asigura implementareacorecta a polimorfismului. In linii generale, aceasta implementare presupuneurmatoarele etape si conventii:

1. Pentru fiecare clasa ce poseda metode virtuale, compilatorul construieste tabela de metode virtuale (Virtual Method Table VMT in Turbo Pascal, Virtual Function Table VFT in Borland C++), ce contine pointeri la punctele de intrare ale acestora (perechi de forma nume_metoda, adresa_punct_de_intrare); 2. Semnatura clasei, pe care constructorul o pune in fiecare obiect este de fapt un pointer la tabela de metode virtuale a clasei obiectului; 3. In cazul legarii dinamice, obiectul este cel care ofera informatia necesara legarii, in el existand referinta la tabela de metode

108

Page 109: Sub Program Are

virtuale ce trebuie consultata pentru a se rezolva apelul. 4. Metodele sunt considerate implicit statice; declararea unei metode virtuale se face explicit.

Revenind la mesajul:send(R,S[,A])

sau R.S(A)

rezolvarea lui se poate discuta in doua ipostaze: - S este un nume de metoda statica; - S este un nume de metoda virtuala.

A. Legarea statica

Legarea statica se realizeaza cand S este o metoda statica. Toate operatiilede mai jos se executa la compilare: 1. se determina clasa C a lui R (din declararea lui); 2. se verifica daca S este o metoda a clasei C (proprie sau mostenita); 3. Daca NU, se va genera un mesaj de eroare (eroare de sintaxa); 4. Daca DA, se verifica lista parametrilor actuali cu declararea metodei (daca exista o lista de parametri actuali si daca limbajul este puternic tipizat); 5. Daca totul este OK, se face legarea statica (mesajul se traduce prin salt la punctul de intrare al metodei gasite).

Observatie

Unele limbaje (C++, Ada) permit existenta unor proceduri cu acelasi nume,care difera prin lista argumentelor (se spune ca numele sunt supraincarcate).In cazul lor, trebuie facuta o verificare in plus: metoda este identificatadupa nume si dupa lista argumentelor.

B. Legarea dinamica

Legarea dinamica este proprie colectiilor de obiecte polimorfice. Prin urmare,se trateaza mesaje de forma:

send(EC,S[,A]) sau EC.S(A)

unde: EC este un element al unei colectii polimorfice; S este o metoda virtuala.

Actiunile se desfasoara in doua momente distincte: compilare si executie.

La compilare: 1. se determina C - clasa de baza a elementelor colectiei EC; 2. se verifica daca S este o metoda (virtuala) a lui C, identificandu-se punctul de intrare in VMT pentru S;

109

Page 110: Sub Program Are

3. Daca DA, se verifica sintaxa apelului; 4. Daca sintaxa apelului este corecta, se pune in cod adresa relativa a metodei S din VMT a lui C.

La executie: 1. se determina C - clasa efectiva a EC (din EC avem adresa VMT; de fapt se identifica VMT a clasei efective, din semnatura pusa in obiectul EC de constructor); 2. se identifica S in VMT determinata la pasul anterior (1), pe baza adresei relative a lui S din VMT, determinata la compilare; 3. din VMT se ia punctul de intrare determinat la pasul 2 si se continua la fel ca la legarea statica.

In explicatiile de mai sus, am considerat ca, deoarece clasa de baza este ceacare defineste protocolul de comunicatie (prin metodele sale virtuale), intabelele de metode virtuale ale claselor derivate o metoda virtuala S va ocupaaceeasi locatie (va avea aceeasi adresa relativa a unui nume de metoda intoate VMT ale unei ierarhii de clase). Prin urmare, la compilare se vadetermina locatia din tabela (adresa relativa) corespunzatoare metodeivirtuale apelate, iar la executie se va determina in care tabela de metodevirtuale se face cautarea.

Revenind la exemplul discutat in 4.1.4, putem spune acum ca pentru a se obtineun calcul corect al pretului de vanzare pentru clasa OUA, metoda Pret_trtrebuie declarata virtuala, iar clasa OUA trebuie sa aiba un constructorspecific (analog clasele FRAGILE si PERISABILE in cazul mostenirii multiple).In aceste conditii, legarea metodei Pret_tr se va face dinamic, cautareametodei virtuale in arborele sau graful de mostenire incepand din clasa OUA.Se observa inca odata utilizarea mostenirii, deosebirea (fata de legareastatica) fiind ca aceasta cautare are loc si in adancime (fiecare cautareincepe din clasa obiectului si nu din clasa in care apare pentru prima datareferirea la caracteristica cautata, in cazul nostru Pret_tr).

4.1.6. Pasi spre POO

Bertrand Meyer [Mey88] defineste conditiile pe care trebuie sa leindeplineasca un produs program si limbaj de programare orientat pe obiecte.In afara primei cerinte, care da criteriul de proiectare, toate celelalte serefera la programare, in sensul restrans al termenului:

Primul nivel corespunde observatiei: datele trebuie sa ofere criteriulfundamental de structurare:

Nivelul 1 (Structura modulara bazata pe obiecte)

Sistemele sunt modularizate pe baza structurilor de date proprii, sau Proiectarea programelor are drept criteriu datele folosite si nu functiile pe care programele trebuie sa le realizeze.

Urmatorul pas realizeaza conectarea cu TAD:

110

Page 111: Sub Program Are

Nivelul 2 (Abstractizarea datelor)

Obiectele trebuie descrise ca implementari ale TAD.

Al treilea pas este de natura mai putin conceptuala si mai mult practica,reflectand o cerinta importanta de implementare: cum se creeaza obiectele.Programatorii nu trebuie sa se preocupe de alocarea sau dealocarea memorieipentru obiecte:

Nivelul 3 (Gestiunea automata a memoriei)

Obiectele neutilizate trebuie dealocate de sistemul de baza (substrat) al limbajului, fara interventia programatorului.

Urmatorul pas este cel care face o separare clara a limbajelor bazate peobiecte de restul lumii. Se poate spune ca ecuatia de definire a acestorlimbaje este identitatea:

clasa = tip de date.

Nivelul 4 (Clase)

Orice tip de date non-simplu este un modul si orice modul de nivel inalt este un tip de date.

Calificatorul "non-simplu" face posibila pastrarea tipurilor de datepredefinite, care nu sunt vazute ca module; cuvantul "nivel inalt" permiteexistenta unitatilor de structurare a unui program (procedurile) care nu sunttipuri.

Urmatorul pas este o consecinta naturala a celui precedent: daca tipurile dedate se identifica cu modulele, suntem tentati sa identificam mecanismele dereutilizare oferite de ambele concepte: - pe de o parte, posibilitatea unui modul de a referi direct entitati definite in alt modul; - pe de alta parte, conceptul de subtip, prin care se poate defini un nou tip adaugand proprietati noi unui tip existent (ca un subdomeniu Integer din Pascal, subtip al lui Integer cu precizarea limitei inferioare si superioare a domeniului sau).

Nivelul 5 (Mostenire)

O clasa poate fi definita ca extensie sau restrictie a alteia.

Tehnicile de mai sus deschid posibilitatea folosirii polimorfismului silegarii dinamice:

Nivelul 6 (Polimorfism si legare dinamica)

Entitatile unui program trebuie sa poata sa refere obiecte din mai multe clase, iar operatiile trebuie sa aiba realizari diferite in clase diferite.

111

Page 112: Sub Program Are

Urmatorul si ultimul pas extinde notiunea de mostenire pentru a permitereutilizarea in mai multe contexte. Aceasta inseamna mostenirea multipla.

Nivelul 7 (Mostenirea multipla si repetata)

Trebuie sa se poata declara clase care sa mosteneasca de la mai multi parinti si/sau de mai multe ori de la aceeasi clasa.

4.2. PROGRAMARE ORIENTATA PE OBIECTE IN TURBO PASCAL4.2.1. Clase si obiecte in Turbo Pascal. Terminologie4.2.2. Mostenire. Obiecte statice4.2.3. Constructori si destructori. Metode statice si virtuale. Legare statica si dinamica. Implementarea obiectelor4.2.4. Proiectarea si implementarea metodelor virtuale4.2.5. Clase abstracte4.2.6. Colectii de obiecte polimorfice

4.2.1. Clase si obiecte in Turbo Pascal. Terminologie

Turbo Pascal (TP) este in acelasi timp un mediu si un limbaj de programareputernic tipizat, ce poseda o extensie POO incepand de la versiunea 5.5.Fiind un mediu de programare binecunoscut utilizatorilor de PC-uri, TurboPascal este accesibil incepatorilor si capabil sa ofere suficienteinstrumente pentru realizarea de aplicatii profesionale. In plus, existaun mare numar de carti si articole de specialitate legate de el. Deoarecelimbajul Pascal este folosit pe scara larga in procesul de invatare aprogramarii calculatoarelor (in scoli si universitati), Turbo Pascalcunoaste o mare popularitate in mediile academice, fiind, prin extensia POO,un mijloc relativ facil de familiarizare cu conceptele programarii orientatepe obiecte. Din punctul de vedere conceptual, extensia POO din Turbo Pascalse inspira din limbajele C++ si Object Pascal.

La prima vedere, ar putea apare o confuzie in notatii. In TP, o clasa estede tipul predefinit 'object' si se defineste (in versiunea 5.5) cu ajutorulmecanismului 'type' (propriu limbajului Pascal), in forma:

type nume_clasa = object[(nume_superclasa)] declaratii de campuri { publice } specificari de metode end;

Conceptual, o clasa este tratata ca un tip virtual de date, pentru ea fiinddefinite campurile (ce formeaza domeniul TVD) si operatiile (metodele).Definitia de mai sus corespunde partii de interfata a unui TVD, ea trebuindcompletata cu partea de implementare a metodelor. La implementare, antetulunei metode este calificat cu numele clasei:

procedure nume_clasa.nume_metoda[(parametri_formali)];sau

function nume_clasa.nume_metoda[(parametri_formali)] : tip;

112

Page 113: Sub Program Are

Instantele unei clase, numite obiecte, sunt declarate ca variabile de tipnume_clasa:

var ob1,ob2 : nume_clasa;

Declaratia de mai sus corespunde unor obiecte statice (alocate in segmentulde date sau in stiva de executie). Folosind pointerii, se poate lucra si cuobiecte dinamice, alocate in heap:

type pointer_nume_clasa = ^nume_clasa;var pob1,pob2 : pointer_nume_clasa;

Apelul unei metode se face printr-un mesaj de forma:

ob1.nume_metoda(lista_de_parametri_actuali);sau

pob1^.nume_metoda(lista_de_parametri_actuali);

in care ob1 (respectiv pob1^) este receptorul, iar numele metodei esteselectorul.

Prin definitia unei clase nume_clasa se specifica si interfata claseirespective cu mediul extern. Regulile de vizibilitate permit ca atatcampurile cat si metodele sa fie vizibile (deci utilizabile) in exterior.Daca pentru metode acest lucru este normal, accesul din exterior la campuri(inclusiv posibilitatea modificarii valorii lor) incalca una din regulilede baza ale abstractizarii datelor:

modificarea valorii campurilor unui obiect trebuie sa se faca numai prin operatiile proprii obiectului respectiv.

Pentru inlaturarea acestui neajuns, in versiunea 6.0 a limbajului TurboPascal exista posibilitatea declararii de metode si campuri private, cesunt invizibile in exterior. Campurile private pot fi referite numai inpartea de implementare a metodelor (publice sau private) ale obiectului,iar metodele private servesc la implementarea celor publice. Schema dedefinire a unei clase de obiecte in Turbo Pascal 6.0 este:

type nume_clasa = object[(nume_superclasa)] declaratii de campuri publice specificari de metode publice private declaratii de campuri private specificari de metode private end;

Pentru protejarea eficienta a campurilor unui obiect, se recomanda ca elesa fie declarate in zona 'private'. Dezavantajul acestei metode deproiectare a claselor de obiecte consta in aceea ca utilizatorul nu va aveaacces la aceste campuri, nici macar pentru a citi valoarea lor. Acest

113

Page 114: Sub Program Are

impediment poate fi surmontat prin adaugarea de metode publice, care safurnizeze valoarea acestor campuri private. In acest fel, campurile devinaccesibile in citire (read only), dar valoarea lor nu se va putea modificadecat prin metode. In specificarea 1 este definita clasa Locatie, cuurmatoarele atribute:

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ campuri: X,Y (private) ³³ metode: Init {modifica campurile X si Y ale obiectului ³³ la valorile specificate prin aX si aY} ³³ XCoord {intoarce valoarea abscisei X} ³³ YCoord {intoarce valoarea ordonatei Y} ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Type { unit-ul ULoc.PAS } Locatie = Object procedure Init(aX,aY:Integer); function XCoord : Integer; function YCoord : Integer; private X,Y : Integer; End;

Specificarea 1. Campuri private si metode publice

Metodele publice XCoord si YCoord au fost concepute tocmai pentru a permitemediului extern (clientului) sa cunoasca valoarea coordonatelor obiectului.

4.2.2. Mostenire. Obiecte statice

In Turbo Pascal mostenirea este simpla. Prin mostenire, campurilesuperclasei se pastreaza si nu pot fi redefinite. In schimb, metodele uneisuperclase se pot redefini in conformitate cu comportamentul specific alsubclasei careia le apartin. In specificarea 2, se considera clasaPunct, descendenta a clasei Locatie definita anterior. Obiectul geometricpunct (in plan) poseda doua coordonate si un atribut de vizibilitate: elpoate fi desenat (este vizibil) sau nu.

type { unit-ul UPctS.PAS } Punct = Object(Locatie) procedure Init(aX,aY:Integer); { rescrie Locatie.Init } procedure Deseneaza; { noua } procedure Ascunde; { noua } function EVizibil : Boolean; { noua } procedure MutaIn(nouX,nouY:Integer); { noua } procedure Translateaza(pas:Integer); { noua } procedure Roteste(centru:Locatie; unghi:Integer); { noua } function Caracteristici: String; { noua } procedure Listeaza; { noua } private

114

Page 115: Sub Program Are

Vizibil : Boolean; { nou } End;

Specificarea 2. Mostenire in Turbo Pascal

Atributele clasei Punct sunt urmatoarele:

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ campuri:X,Y (private si mostenite de la Locatie) ³³ Vizibil (privat) ³³ metode: Init {redefineste metoda Init de la Locatie ³³ initializeaza in plus campul Vizibil} ³³ XCoord, YCoord {mostenite de la Locatie} ³³ Deseneaza {desenarea obiectului pe ecran} ³³ Ascunde {stergerea obiectului de pe ecran} ³³ EVizibil {intoarce valoarea campului Vizibil} ³³ MutaIn {muta obiectul in locatia nouX, nouY} ³³ Translateaza {efectueaza o translatie, marind ³³ valorile coordonatelor cu valoarea pas} ³³ Roteste {roteste obiectul in jurul locatiei ³³ centru, cu unghiul unghi} ³³ Caracteristici {intoarce un sir de caractere ce ³³ contine valorile coordonatelor} ³³ Listeaza {afiseaza caracteristicile obiectului} ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

In aceasta specificare, metoda Init a clasei Punct redefineste metodaomonima de la superclasa Locatie, concretizand comportamentul specific aldescendentului, iar metoda EVizibil permite cunoasterea valorii campuluiVizibil in mediul extern.

Type Cerc = Object(Punct) procedure Init(aX,aY,aRaza:Integer); procedure Deseneaza; procedure Ascunde; procedure MutaIn(nouX,nouY:Integer); procedure Translateaza(pas:Integer); procedure Roteste(centru:Locatie; unghi:Integer); procedure Mareste(CuCit:Integer); function Raza : Integer; function Caracteristici : String; private R : Integer; End;

Specificarea 3. Rafinarea mostenirii

O ilustrare mai cuprinzatoare a redefinirii metodelor este prezentata inspecificarea 3, corespunzatoare clasei Cerc, descendenta a clasei Punct.

115

Page 116: Sub Program Are

Conceptual, obiectul geometric cerc are ca invariant un obiect geometricpunct, numit centrul cercului, prin urmare mostenirea este naturala. Inafara centrului, cercul poseda o raza (atribut specific). Atributele claseiCerc sunt urmatoarele:

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ campuri: X,Y,Vizibil (private si mostenite de la Punct) ³³ R (raza, privat) ³³ metode: Init {redefineste metoda Init de la Punct ³³ initializeaza in plus campul R} ³³ XCoord {mostenita de la Locatie} ³³ YCoord {mostenita de la Locatie} ³³ EVizibil {mostenita de la Punct} ³³ Listeaza {mostenita de la Punct} ³³ Deseneaza {trasarea obiectului pe ecran ³³ redefineste metoda omonima de la Punct} ³³ Ascunde {stergerea obiectului de pe ecran ³³ redefineste metoda omonima de la Punct} ³³ MutaIn {muta obiectul in locatia definita ³³ de coordonatele nouX, nouY ³³ redefineste metoda omonima de la Punct} ³³ Translateaza {efectueaza o translatie, marind ³³ valorile coordonatelor cu pas ³³ redefineste metoda omonima de la Punct} ³³ Roteste {roteste obiectul in jurul locatiei ³³ centru, cu unghiul unghi ³³ redefineste metoda omonima de la Punct} ³³ Mareste {mareste raza cercului cu ³³ valoarea parametrului pas} ³³ Raza {intoarce valoarea campului R} ³³ Caracteristici {intoarce un sir de caractere ce ³³ contine valorile coordonatelor si razei} ³³ redefineste metoda omonima de la Punct} ³ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Metoda Raza permite cunoasterea valorii razei cercului in mediul extern.Metodele Init, Deseneaza, Ascunde, MutaIn, Roteste si Translateaza suntredefiniri ale metodelor omonime ale superclasei Punct, ele concretizandcomportamentul specific al clasei Cerc, diferit de cel al parintelui sauPunct. Astfel, desenarea unui cerc inseamna desenarea centrului si acircumferintei; stergerea trebuie sa le ascunda. Mutarea unui cerc (de faptmutarea centrului sau) presupune stergerea vechii imagini, mutareacentrului si desenarea noii imagini a obiectului. In mod analog seprocedeaza si in cazul translatiei sau rotatiei obiectului Cerc. Acestlucru va rezulta cu exactitate din implementarea claselor Locatie, Punct siCerc, care este data in continuare. Se observa gradul ridicat desimplitate al algoritmilor folositi.

Exemplul 1. Unit-ul ULOC.PAS: Implementarea metodelor clasei Locatie

116

Page 117: Sub Program Are

Exemplul 2. Unit-ul UPctS.PAS: Implementarea metodelor clasei Punct

Exemplul 3. Unit-ul UCercS.PAS: Implementarea metodelor clasei Cerc

In implementarea metodelor claselor mai sus definite s-au folosit proceduri sifunctii din doua unit-uri: Graph (unit sistem, ce contine extensia grafica alimbajului Turbo Pascal) si UGraph (unit utilizator). Pentru o mai usoaraaccesare a claselor enumerate, s-au conceput trei unit-uri Turbo Pascal: ULocpentru clasa Locatie, UPctS pentru clasa Punct si UCercS pentru clasa Cerc.Fiecare unit contine in partea de interfata definitia clasei, iar in parteade implementare textele sursa ale metodelor.

Utilizarea instantelor claselor Cerc si Punct este prezentata in programeleExObSt1.PAS si ExObSt2.PAS. Primul dintre ele lucreaza cu obiecte statice, iaral doilea cu obiecte dinamice.

Obiectele din clasele Punct si Cerc reactioneaza diferit la mesajeleDeseneaza, Ascunde, MutaIn, Roteste, Translateaza, Listeaza. Numele metodelorapelate este acelasi pentru o actiune data, indiferent de obiect (punct saucerc), sugerand actiunea ce trebuie efectuata. Evident, metodele vor aveaimplementari diferite pentru cele doua clase Punct si Cerc. Ar fi interesantde vazut daca se poate lucra cu colectii de astfel de obiecte polimorfe,colectii care contin pointeri la obiectele respective. Un exemplu simplu esteprogramul ExObSt3.PAS, ce reprezinta transpunerea programului ExObSt2.PAS,colectia considerata aici fiind un tablou A de doua elemente, cu elementul detablou de tip PointerLaPunct.

Din pacate, desi programele ExObSt2 si ExObSt3 sunt echivalente semantic(cel putin la prima vedere), la executie ele se comporta diferit, in sensulca al doilea obiect (referit de A[2]), chiar daca este instanta a clasei Cerc,se comporta ca o instanta a clasei Punct. "Necazul" este generat deinstructiunea de atribuire:

A[2] := PC;

si nu poate fi evitat nici printr-un subterfugiu de forma:

New(A[2]); {alocarea obiectului dinamic referit de A[2]} A[2]^.Init(150,230,50);

deoarece protesteaza compilatorul (A[2] este PointerLaPunct, iar metoda Initdin urmatoarea instructiune apartine clasei Cerc). Acest program este o primatentativa de lucru cu obiecte polimorfice. Reamintim ca aceste obiecte suntinstante ale unor clase diferite, untre care exista relatia de mostenire sicare pot sa raspunda la aceleasi mesaje. Se pune intrebarea:

Cum se rezolva lucrul cu obiecte polimorfice in Turbo Pascal?

Analizand textele sursa ale metodelor clasei Cerc ce sunt redefiniri alemetodelor clasei Punct, constatam ca MutaIn, Translateaza, Roteste si Listeazasunt identice din punctul de vedere al apelului (este clar ca, semantic,actiunile sunt diferite). Se pune intrebarea:

117

Page 118: Sub Program Are

Nu s-ar putea ca metodele amintite sa fie mostenite de la clasa Punct?

Raspunsul la aceste doua intrebari este afirmativ. In Turbo Pascal, ca intoate limbajele orientate pe obiecte, acest lucru se realizeaza prin actiuneaconjugata a celor trei mecanisme discutate in sectiunea 4.1.5: constructoriisi destructorii, metodele virtuale si legarea dinamica. Pe langa acestea siin stransa legatura cu folosirea constructorilor, s-a prevazut si o extensiea procedurilor standard New si Dispose. Sa le discutam pe rand.

4.2.3. Constructori si destructori. Metode statice si virtuale. Legare statica si dinamica. Implementarea obiectelor

In exemplele discutate pana acum, mostenirea a fost folosita pentru campuri(clasele Punct si Cerc mostenesc de la Locatie campurile X si Y) si pentrumetode (ale superclasei, care nu sunt redefinite in subclase: XCoord, YCoord).Metode omonime (Deseneaza, Ascunde, MutaIn, Translateaza, Roteste) au fostdefinite in clasa Punct si au fost redefinite (toate) in clasa derivata, Cerc.

Mai mult, fiecare apel de metoda este rezolvat la compilare, in sensul ca sededuce, din tipul receptorului, clasa a carei metoda este apelata. Din acestmotiv, in programul ExObSt3 nu functioneaza polimorfismul dinamic: desi PCeste pointer la cerc si obiectul referit de PC este initializat ca atare, prinatribuirea A[2] := PC are loc o constrangere (elementele tabloului A suntpointeri la punct) si in continuare compilatorul va considera ca A[2] referaun punct, selectand in consecinta metodele clasei Punct la intalnireareceptorului A[2]^. Aceasta maniera de legare a metodei la tipul claseiinstantei a fost numita in 4.1.5.2 legare statica.

Toate metodele prevazute in exemplele discutate pana acum sunt metode statice(implicit, orice metoda este statica in Turbo Pascal). Cand un mesaj contineapelul unei metode statice cu selectorul nume_metoda, apelul se poate rezolvala compilare prin parcurgerea arborelui de mostenire de la clasa obiectuluireceptor (cunoscuta la compilare) spre radacina arborelui. Prima metoda gasitace are ca selector nume_metoda va fi metoda apelata si acest apel se rezolvain mod obisnuit (ca apelul unei proceduri din Pascal Standard). Algoritmic,legarea statica pentru mesaje de forma

ob1.nume_metoda_statica(lista_de_parametri_actuali);sau

pob1^.nume_metoda_statica(lista_de_parametri_actuali);

inseamna:

1. Se determina clasa receptorului (ob1 sau pob1^), cunoscuta la compilare (ob1 sau pob1 sunt declarate ca variabile); 2. Se selecteaza din dictionarul de metode al clasei respective adresa punctului de intrare al metodei specificate; 3. Se pune in cod apelul metodei respective (salt la punctul de intrare aferent procedurii in cauza).

Din punctul de vedere al implementarii, pentru o instanta a unei clase se

118

Page 119: Sub Program Are

aloca un numar de octeti egal cu suma lungimilor de reprezentare alecampurilor instantei (la fel ca la TD record). In cazul exemplelor de mai sus,se vor aloca 5 octeti pentru o instanta a clasei Punct:

SizeOf(Punct) = SizeOf(X) + SizeOf(Y) + SizeOf(Vizibil),

respectiv 7 octeti pentru o instanta a clasei Cerc:

SizeOf(Cerc) := SizeOf(Punct) + SizeOf(R).

Prin urmare, in timpul executiei, o instanta nu-si "cunoaste" clasa sa. Acestlucru este valabil in situatia in care clasele nu poseda metode constructor,toate exemplele anterioare fiind din aceasta categorie. Rolul unui constructoreste in primul rand acela de a atasa fiecarei instante "semnatura" clasei lacare aceasta apartine. In Turbo Pascal acest lucru se realizeaza atasandinca doi octeti reprezentarii fiecarei instante. In spatiul astfel rezervat,constructorul va pune un pointer la tabela de metode virtuale (VMT) a claseila care apartine instanta. Informatia de instantiere este accesibila numaiin citire, ea putand fi modificata doar printr-un alt constructor. Reamintimca, pe langa aceasta functie (principala in POO), un constructor are deobicei sarcina de a initializa campurile instantei (rolul metodelor Init dinexemplele discutate) sau de a aloca memorie dinamica pentru instanta saupentru acele campuri ale acesteia ce refera variabile dinamice.

Distrugerea informatiei de instantiere se face printr-o alta metoda speciala,numita destructor in Turbo Pascal. Pe langa aceasta sarciba, un destructorpoate realiza dealocarea memoriei dinamice alocata de constructor.

O clasa poate avea mai multi constructori si destructori. Specificarea lorse face in definitia clasei, in forma:

constructor nume_constructor[(parametri_formali)];si

destructor nume_destructor[(parametri_formali)]; [virtual;]

iar implementarea lor se face in aceeasi maniera ca si implementareacelorlalte metode, antetele fiind:

constructor nume_clasa.nume_constructor[(parametri_formali)]respectiv

destructor nume_clasa.nume_destructor[(parametri_formali)];

Declararea metodelor virtuale se face atasand cuvantul cheie 'virtual' laspecificarea acestora, in maniera:

procedure nume_metoda[(parametri_formali)]; virtual;sau

function nume_metoda[(parametri_formali)] : tip; virtual;

Inainte de a discuta rolul metodelor virtuale, sa precizam restrictiile deproiectare si utilizare a lor, date sub forma urmatoarelor reguli (deduse dinregulile R1-R6 prezentate in sectiunea 4.1):

119

Page 120: Sub Program Are

V1. Orice clasa ce poseda metode virtuale trebuie sa aiba cel putin un constructor. V2. In programul utilizator, o instanta isi va apela constructorul inaintea oricarui apel de metoda virtuala. V3. Orice specializare (rescriere) a unei metode virtuale in descendentii clasei trebuie sa fie tot virtuala; V4. Orice specializare (rescriere) a unei metode virtuale in descendentii clasei trebuie sa aiba exact acelasi antet (acelasi nume si aceeasi lista de parametri). V5. Constructorii nu pot fi virtuali.

Pentru fiecare clasa ce poseda metode virtuale, compilatorul Turbo Pascalcreeaza o tabela de metode virtuale (Virtual Method Table, VMT), ce contine: - dimensiunea unei instante a clasei; - numarul de metode virtuale; - pointeri la codul fiecarei metode (adrese de puncte de intrare).

Asadar, VMT este proprie clasei. Am vazut mai inainte ca fiecare instantacreata cu un constructor poarta in ea "semnatura" clasei prototip, care nueste altceva decat adresa VMT.

Apartenenta unei instante 'ob' la o clasa data 'nume_clasa' se poate testain Turbo Pascal astfel:

TypeOf(ob) = TypeOf(nume_clasa);

iar faptul ca doua obiecte ob1 si ob2 sunt instantee ale aceleiasi clase prin:

TypeOf(ob1) = TypeOf(ob2);

Am vazut in 4.1.5 ca metodele virtuale servesc la modelarea comportamentuluipolimorfic al obiectelor. Daca apelul metodelor statice se rezolva lacompilare, apelul de metode virtuale se rezolva numai la executie. Astfel,concretizand consideratiile prezentate in 4.1.5, un mesaj de forma:

ob1.nume_metoda_virtuala(lista_de_parametri_actuali);sau

pob1^.nume_metoda_virtuala(lista_de_parametri_actuali);

se rezolva la executie in urmatoarele etape:

1. Se determina clasa receptorului (ob1 sau pob1^), de fapt adresa VMT corespunzatoare clasei sale, din informatia existenta in instanta respectiva; 2. Se selecteaza din VMT adresa codului metodei specificate (adresa punctului de intrare); 3. Se lanseaza in executie respectiva metoda (se face salt la punctul de intrare corespunzator acesteia).

Spre deosebire de legarea statica, unde conteaza doar tipul clasei instantei(cunoscut la compilare), aceasta maniera de legare a selectorului de receptor

120

Page 121: Sub Program Are

in timpul executiei a fost numita in 4.1.5.2 'legare dinamica'. Accentuamfaptul ca se apeleaza metoda adecvata instantei, in conformitate cu atributeleei din timpul executiei, si nu metoda adecvata clasei statice a instantei,declarata la compilare prin 'var'. Un obiect 'o', instanta a unei clase 'c',poate fi considerat in dOUA ipostaze distincte:

a) la compilare, cand i se cunoaste clasa statica (declarata cu 'var o:c;' in Turbo Pascal); in acest moment se pot rezolva toate apelurile de metode statice 'ms' prin: 'o.ms(...);' b) la executie, cand i se cunoaste clasa efectiva 's_c' (ca urmare a apelului unui constructor, de exemplu 'Init' al clasei 's_c', care va trebui sa fie o extensie a clasei 'c' (in arborele de mostenire al clasei statice 'c'): 'o.Init(...);' dupa acest moment (al apelului de constructor), orice apel de metoda virtuala 'mv' cu receptorul 'o': 'o.mv(...);' va tine cont de clasa efectiva 's_c' si nu de clasa statica 'c'.

Ipostaza a) corespunde legarii statice, pe cand b) corespunde legariidinamice. Cunoscand aceste lucruri, se poate explica necesitatea regulilorV1-V5 enuntate anterior:

V1. Prezenta imperativa a constructorului pentru clasele ce poseda metode virtuale: daca nu exista constructor, instanta nu va contine spatiu pentru adresa VMT, deci nu se va putea cunoaste clasa efectiva a instantei; V2. Apelul constructorului unei instante 'o' trebuie sa preceada apelul oricarei metode virtuale a lui o: numai dupa apelul constructorului se cunoaste clasa efectiva a instantei; V3. Orice rescriere a unei metode virtuale trebuie sa fie la randul ei metoda virtuala: absenta acestei reguli ar provoca combinarea celor doua modalitati de legare a metodelor, complicand implementarea legarii statice si dinamice; V4. O metoda virtuala cu numele 'mv' trebuie sa aiba aceeasi lista de parametri in toate clasele din arborele de mostenire in care ea este redefinita: acest lucru este impus de caracterul puternic (static) tipizat al Pascal-ului (antetul metodei din clasa statica, de declarare, asupra careia se face verificarea la compilare, trebuie sa fie identic cu antetul metodei din clasa efectiva, dinamica care se va apela la executie). V5. Constructorii sunt proprii fiecarei clase, deci nu pot fi virtuali; doua clase diferite au tabele de metode virtuale diferite.

4.2.4. Proiectarea si implementarea metodelor virtuale

Vom relua specificatiile claselor Punct si Cerc in care vom folosi metodevirtuale. Conform precizarilor V1-V5, aceasta inseamna ca in fiecare clasa vorfi prevazute: - un constructor; - un destructor (necesar pentru dealocarea obiectelor dinamice); - metode virtuale.Inainte de a discuta noile specificatii, ne vom referi la lucrul cu obiectedinamice in Turbo Pascal.

121

Page 122: Sub Program Are

Un obiect dinamic este alocat in memoria dinamica (heap), fiind referit de unpointer ce contine adresa lui. Programele ExObSt2 si ExObSt3 contin exemple deobiecte dinamice din clasele Punct si Cerc. Uzual, in situatia:

type pointer_nume_clasa = ^nume_clasa;var pob : pointer_nume_clasa;

crearea unui obiect dinamic se face cu ajutorul unei proceduri de initializare(numita Init in exemplul nostru), in maniera:

New(pob); {se aloca memorie dinamica pentru obiectul dinamic referit de pob}

pob^.Init(...); {se apeleaza metoda Init}

In versiunea 6.0, Turbo Pascal introduce extensii ale procedurilor standardNew si Dispose, care se pot apela in cazul existentei constructorilor,respectiv destructorilor. Astfel, extensiile au sintaxa (Init esteconstructorul clasei nume_clasa, iar Done este destructorul acesteia):

New(pob,Init(...));

respectiv

Dispose(pob,Done(...));

Actiunile efectuate de procedura standard New in exemplul de mai sus sunt: 1. Se aloca in memoria dinamica spatiu pentru obiectul dinamic referit de pob, in conformitate cu specificatiile clasei nume_clasa; 2. Se apeleaza cu pob^ ca receptor constructorul Init, care: 2.1. pune in instanta pob^ adresa VMT a clasei nume_clasa; 2.2. initializeaza (si eventual aloca dinamic unele dintre) campurile obiectului pob^.Reciproc, procedura standard Dispose (versiunea extinsa) realizeaza: 1. Apelarea destructorului Done (cu pob^ ca receptor), care: 1.1. reseteaza pe valorile implicite (si eventual dealoca unele dintre) campurile pob^; 1.2. distruge in instanta pob^ adresa VMT a clasei nume_clasa; 2. Dealocarea din memoria dinamica a spatiului pentru obiectul pob^.

Procedura standard New mai are o extensie, cu sintaxa:

pob := New(pointer_nume_clasa,Init(...));

caz in care New va intoarce un pointer de tip pointer_nume_clasa, efectuandactiunile precizate anterior. Bineinteles, Init trebuie sa fie constructorulclasei nume_clasa. Se observa ca in aceasta situatie apelul lui New aresintaxa apelului de functie si nu de procedura.

Revenind la specificatiile claselor Punct si Cerc si tinand cont deobservatiile facute, acestea se vor scrie astfel:

122

Page 123: Sub Program Are

type PointerLaPunct = ^Punct; Punct = Object(Locatie) constructor Init(aX,aY:Integer); destructor Done; virtual; procedure Deseneaza; virtual; procedure Ascunde; virtual; function EVizibil : Boolean; procedure MutaIn(nouX,nouY:Integer); procedure Translateaza(pas:Integer); procedure Roteste(centru:Locatie; unghi:Integer); function Caracteristici: String; virtual; procedure Listeaza(c,l:Integer); private Vizibil : Boolean; End; PointerLaCerc = ^Cerc; Cerc = Object(Punct) constructor Init(aX,aY,aRaza:Integer); procedure Deseneaza; virtual; procedure Ascunde; virtual; procedure Mareste(CuCit:Integer); function Raza : Integer; function Caracteristici : String; virtual; private R : Integer; End;

Specificarea 4. Metode virtuale, constructori si destructori

Implementarea metodelor este data in unit-urile UPctD.PAS si UCercD.PAS.

Exemplul 4: unit-urile UPctD.PAS si UCercD.PAS

Comparand specificarea 4 cu specificarea 3 (pentru clasa Cerc), se constata ungrad mare de reutilizare a codului, in sensul ca metodele MutaIn, Roteste,Translateaza si Listeaza nu mai apar ca metode proprii ale acestei clase, elefiind mostenite de la clasa parinte, Punct. Deoarece toate aceste metodeapeleaza la randul lor metode virtuale, legarea acestora se va face dinamic.De exemplu, in situatia:

var P : Punct; C : Cerc;

mesajul P.MutaIn(100,200) va avea ca efect (programul ExObSt2):

P.Ascunde; {Punct.Ascunde} Locatie.Init(100,200); P.Deseneaza {Punct.Deseneaza}

pe cand mesajul C.MutaIn(100,200) se va traduce astfel:

123

Page 124: Sub Program Are

C.Ascunde; {Cerc.Ascunde} Locatie.Init(100,200); C.Deseneaza {Cerc.Deseneaza}

Mai mult, cand se lucreaza cu obiecte polimorfice, ca in situatia:

var A:Array[1..2] of PointerLaPunct;

si A[1] := New(PointerLaPunct,Init(100,200)); A[2] := New(PointerLaCerc, Init(150,230,50));

mesajul A[1]^.MutaIn(100,200) va avea ca efect:

A[1]^.Ascunde; {Punct.Ascunde} Locatie.Init(100,200); A[1]^.Deseneaza{Punct.Deseneaza}

pe cand mesajul A[2]^.MutaIn(100,200) se va traduce astfel:

A[2]^.Ascunde; {Cerc.Ascunde} Locatie.Init(100,200); A[2]^.Deseneaza{Cerc.Deseneaza}

Se observa clar, in cazul obiectelor polimorfice, maniera in care sestabileste la executie metoda virtuala adecvata instantei receptorului.

Programele ExObSt1, ExObSt2 si ExObSt3 prezentate anterior vor functionacorect in prezenta noilor specificatii ale clasei Cerc. In fond, nu este vorbade modificarea algoritmilor care implementeaza metodele acestei clase, ci deo utilizare mai eficienta a mostenirii prin utilizarea metodelor virtuale.De aceasta data si programul ExObSt3 va functiona corect (exemplul arenumele ExObSt4).

4.2.5. Clase abstracte

In proiectarea ierarhiei claselor, exista unele dintre ele care nu vor posedainstante. Rolul lor este o mai buna structurare a acestei ierarhii, elecolectand caracteristicile comune (campuri si metode) pentru mai multe clasedescendente. Clasa Locatie din specificarea 1 este un exemplu de clasaabstracta. Rolul ei este definirea caracteristicilor (coordonatele, metodelede initializare si de accesare a acestora) comune pentru clasele descendente.Toate aceste clase, avand clasa Locatie in arborele lor de mostenire, vorposeda atributele respective.

Pe de alta parte, Locatie poate fi considerata o clasa abstracta si deoarececomportamentul ei nu este suficient de bogat pentru a fi utilizat deinstantele sale. Astfel, Locatie nu poseda metode de vizualizare saumanipulare, cum poseda descendentul sau direct, clasa Punct.

4.2.6. Colectii de obiecte polimorfice

124

Page 125: Sub Program Are

4.2.6.1. Liste

In programul 4.2.3 este prezentat un exemplu simplu de colectie de obiectepolimorfice, depozitate in tabloul A cu doua elemente de tip PointerLaPunct.O alta maniera de implementare a unei colectii de obiecte va folosi o listasimplu inlantuita. Intreaga colectie poate fi considerata ca o noua clasa,asupra careia se prelungesc operatiile proprii obiectelor ei. In unit-ulUListF.PAS este prezentata specificarea si implementarea clasei Lista.

Exemplul 5: Unit-ul UListF.PAS

In afara claselor mentionate mai inainte, in acest exemplu se folosesc incadoua: Arc - clasa derivata a clasei Cerc, a carei specificare si implementare este prezentata in unit-ul UArc; Segment - clasa derivata a clasei Punct, prezentata in unitul USeg.

Exemplul 4.2.6: Unit-ul UArc.PAS

Exemplul 4.2.7. Unit-ul USeg.PAS

Metodele clasei Lista se impart in doua categorii: - metode proprii listelor: Init - constructor, creeaza o lista vida; Done - destructor, dealoca elementele listei; Adauga - adauga un nou element la coada listei; - metode ce vor reflecta comportamentul obiectelor memorate in elementele listei: Listeaza - listeaza caracteristicile fiecarui element din lista; Deseneaza - deseneaza fiecare obiect din lista; Ascunde - ascunde fiecare obiect din lista; Roteste - roteste fiecare obiect din lista cu acelasi unghi, in jurul obiectului precedent din lista (prin parcurgerea circulara a listei); Translateaza - translateaza fiecare obiect din lista cu aceeasi marime; Mareste - mareste raza fiecarui obiect rotund din lista cu aceeasi marime.

A doua categorie de metode foloseste din plin apelul de metode virtuale, fiedirect, fie indirect. O atentie speciala trebuie acordata metodei Mareste,care nu este comuna tuturor claselor mentionate, fiind proprie numai claselorCerc si Arc (prototipuri de obiecte rotunde). In implementarea ei se folosestefunctia TypeOf pentru determinarea clasei efective a obiectului. O altasolutie de implementare ar fi adaugarea metodei virtuale Mareste la clasaPunct (la care ea n-ar face nimic sau eventual ar desena obiectul), caz incare implementarea metodei Mareste a clasei s-ar face in aceeasi maniera casi metodele anterioare. Aceasta se poate realiza astfel: - la clasa Punct se declara metoda virtuala Mareste, care se implementeaza astfel:

Procedure Punct.Mareste(CuCit:Integer); Begin

125

Page 126: Sub Program Are

Deseneaza End; { Mareste }

- la clasa Cerc se declara Mareste ca virtuala; - la clasa Lista se modifica implementarea metodei Mareste astfel:

Procedure Lista.Mareste(CuCit:Integer); Var N : PointerLaNod; PP : PointerLaPunct; Begin N := Ultimul; While N <> Nil do Begin PC := N^.Element; PC^.Mareste(CuCit) N := N^.Precedentul End; End; { Mareste }

Lucrul cu obiecte polimorfe este ilustrat in programul ExObDi.PAS

4.2.6.2. Unitul UGraph

Pentru implementarea unor functii grafice specifice exemplelor discutate anterior, s-a proiectat unit-ul UGraph, ce exporta o serie de astfel de operatii. Textul sursa este prezentat in unit-ul UGraph.PAS.

8. Grafica in Turbo Pascal

Elemente de discutie

8.1. Utilizarea ecranului in mod grafic8.2. Unit-ul Graph8.3. Desenarea in plan a figurilor din planul real

8.1. Utilizarea ecranului in mod grafic

Etape - initializarea modului grafic (trecerea de la modul text la modul grafic) - lucrul in modul grafic - terminarea modului grafic (revenirea la modul text)

Memoria ecran: zona de memorie interna ce contine ceea ce se vede pe ecran - text - grafica

Caracteristicile modului de lucru grafic - primitive grafice - culori - rezolutie - placa grafica

126

Page 127: Sub Program Are

- Borland Graphics Interface (BGI)

0 x GetMaxX-1 0ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ ³ ³ ³ ³ ³ ³ yÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ.P(x, y) ³ ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙGetMaxY-1

Pixel - picture element - coordonate (numere intregi) - culoare

Fereastra fizica ecran (viewport) - portiune din ecranul grafic in care se realizeaza desenul

0 u1 u2 GetMaxX-1 0ÚÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ P1(u1, v1) ³ ³ v1ÅÄÄÄÄÄÄÄ ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ³ ³ º º ³ ³ º º ³ ³ º º ³ v2ÅÄÄÄÄÄÄÄ ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ³ ³ P2(u2, v2) ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙGetMaxY-1

- caracteristicile ferestrei fizice - coordonatele aparat: (u, v) sunt relative la originea P1 - initial: fereastra fizica este tot ecranul

8.2. Unit-ul Graph8.2.1. Terminologie8.2.2. Clase de subprograme8.2.3. Exemple de folosireAcest unit contine: - subprograme - constante - variabilespecifice modului de lucru grafic.

8.2.1. Terminologie - ecranul grafic corespunde unui driver grafic - driverul grafic - corespunde placii grafice din calculator

127

Page 128: Sub Program Are

- este specificat printr-o constanta simbolica cu urmatoarele valori

Constanta ³ Valoare/comentariu ÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ CurrentDriver ³ -128 pentru GetModeRange Detect ³ 0 Provoaca autodetectie CGA ³ 1 MCGA ³ 2 EGA ³ 3 EGA64 ³ 4 EGAMono ³ 5 IBM8514 ³ 6 HercMono ³ 7 ATT400 ³ 8 VGA ³ 9 PC3270 ³ 10

- modul grafic - un driver grafic poate avea mai multe moduri grafice - la un moment dat pentru driverul grafic existent se lucreaza intr-un singur mod grafic - modul grafic defineste - rezolutia pe verticala si pe orizontala (GetMaxX, GetMaxY) - numarul de culori (GetMaxColor) - modul grafic se reprezinta printr-un numar (constanta simbolica)

Constanta ³ Valoare³ Rezolutie Constanta ³Valoare³ RezolutieÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ ÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍCGAC0 ³ 0 ³ 320 x 200 EGALo ³ 0 ³ 640 x 200CGAC ³ 1 ³ 320 x 200 EGAHi ³ 1 ³ 640 x 350CGAC2 ³ 2 ³ 320 x 200 ³ ³CGAC3 ³ 3 ³ 320 x 200 EGA64Lo ³ 0 ³ 640 x 200CGAHi ³ 4 ³ 640 x 200 EGA64Hi ³ 1 ³ 640 x 350 ³ ³ ³ ³MCGAC0 ³ 0 ³ 320 x 200 ATT400C0 ³ 0 ³ 320 x 200MCGAC1 ³ 1 ³ 320 x 200 ATT400C1 ³ 1 ³ 320 x 200MCGAC2 ³ 2 ³ 320 x 200 ATT400C2 ³ 2 ³ 320 x 200MCGAC3 ³ 3 ³ 320 x 200 ATT400C3 ³ 3 ³ 320 x 200MCGAMed ³ 4 ³ 640 x 200 ATT400Med ³ 4 ³ 640 x 200MCGAHi ³ 5 ³ 640 x 480 ATT400Hi ³ 5 ³ 640 x 400 ³ ³ ³ ³EGAMonoHi ³ 3 ³ 640 x 350 IBM8514Lo ³ 0 ³ 640 x 480HercMonoHi ³ 0 ³ 720 x 348 IBM8514Hi ³ 1 ³ 1024 x 768 ³ ³ ³ ³VGALo ³ 0 ³ 640 x 200 PC3270Hi ³ 0 ³ 720 x 350VGAMed ³ 1 ³ 640 x 350 VGAHi ³ 2 ³ 640 x 480

- informatii de desenare - culoarea de fond - culoarea fondului (GetBkColor) - culoarea cernelii - culoarea cu care se deseneaza (GetColor) - punctul curent (GetX, GetY) - punctul in care se afla cursorul grafic

128

Page 129: Sub Program Are

- pagini grafice - pentru driverele EGA (256K), VGA si Hercules, memoria grafica se imparte in mai multe pagini - fiecare pagina este identificata printr-un numar - pagina curenta este pagina care este afisata pe ecran (SetVisualPage) - pagina activa este pagina in care se face desenarea (SetActivePage) - pagina curenta si pagina activa nu sunt neaparat una si aceeasi - la animatie se procedeaza astfel: - se seteaza ca pagina activa alta pagina decat cea curenta si se deseneaza in ea - cand pagina este desenata, se declara ca pagina curenta - se pot folosi mai multe pagini, care se declara pe rand pagini curente - culorile pentru modul curent se codifica prin numere intregi prin urmatoarele constante simbolice

Culori intunecate: Culori deschise: (Cerneala si fond) (Fond) ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Black 0 DarkGray 8 Blue 1 LightBlue 9 Green 2 LightGreen 10 Cyan 3 LightCyan 11 Red 4 LightRed 12 Magenta 5 LightMagenta 13 Brown 6 Yellow 14 LightGray 7 White 15

- paleta de culoare: un tabel de culori care contine codurile acestora, intr-o anumita ordine - numarul de culori depinde de driverul grafic si de modul grafic - se poate modifica ordinea culorilor in paleta - se obtin efecte speciale - exista o paleta curenta, care se poate seta (SetPalette) - factor de corectie (aspect ratio) - este definit de doi parametri: XAsp si YAsp cu semnificatia ca o linie orizontala de dimensiune XAsp are pe verticala dimensiunea YAsp - stil de umplere (fill style), se modifica cu SetFillStyle - este folosit pentru umplerea zonelor ecranului grafic - este definit de - sablon de umplere (fill pattern) - culoare de umplere (fill color) - constante simbolice pentru stilul de umplere

Constanta ³ Valoarea³ Semnificatia ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ EmptyFill ³ 0 ³ Foloseste culoarea fondului SolidFill ³ 1 ³ Foloseste culoarea cernelii LineFill ³ 2 ³ Umplere cu --- LtSlashFill ³ 3 ³ Hasura /// SlashFill ³ 4 ³ Hasura /// fina

129

Page 130: Sub Program Are

BkSlashFill ³ 5 ³ Hasura \\\ fina LtBkSlashFill ³ 6 ³ Hasura \\\ HatchFill ³ 7 ³ Light hatch fill XHatchFill ³ 8 ³ Heavy cross hatch InterleaveFill ³ 9 ³ Linii intretesute WideDotFill ³ 10 ³ Puncte spatiate larg CloseDotFill ³ 11 ³ Puncte spatiate strans UserFill ³ 12 ³ Definit de utilizator

- o linie desenata are urmatoarele caracteristici (GetLineSettings) - stil de linie (line style) SetLineStyle - sablon de linie (line pattern) - grosime de linie (line thickness) - constante simbolice:

Stiluri de linie Grosimi de linie SolidLn 0 continua NormWidth 1 normala DottedLn 1 puncte ThickWidth 3 subtire CenterLn 2 DashedLn 3 intrerupta UserBitLn 4 (Definit de utilizator)

- textul scris in modul grafic are urmatoarele caracteristici de stil (GetTextSettings) - font - fontul cu care se scrie - directie - directia in care se scrie - dimensiunea caracterelor - proportia acestora pe orizontala si verticala

Constanta ³Valoare³ Semnificatie ÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ DefaultFont ³ 0 ³ font mapat 8x8 bit TriplexFont ³ 1 ³ Stroked font SmallFont ³ 2 ³ Stroked font SansSerifFont ³ 3 ³ Stroked font GothicFont ³ 4 ³ Stroked font HorizDir ³ 0 ³ Orientare de la stanga la dreapta VertDir ³ 1 ³ Orientare de jos in sus UserCharSize ³ 0 ³ Dimensiunea caracterelor definita de ³ ³ utilizator

8.2.2. Clase de subprograme

- initializarea si terminarea modului grafic - CloseGraph - termina modul grafic - DetectGraph - determina driverul grafic - GraphErrorMsg - intoarce mesajul de eroare pentru un cod specificat - GraphResult - intoarce starea ultimei operatii grafice efectuate = 0 OK, <> 0 - eroare - InitGraph - initializeaza modul grafic - RestoreCrtMode - restaureaza modul ecran dinainte de trecerea in modul grafic

130

Page 131: Sub Program Are

- setarea ferestrei fizice - ClearDevice - sterge ecranul si seteaza punctul curent CP la (0,0) - ClearViewPort - sterge fereastra fizica curenta - GetViewSettings - obtine parametrii ferestrei fizice - SetViewPort - seteaza fereastra fizica curenta - SetVisualPage - stabileste pagina vizuala curenta - setari de pagina, culoare, factor de corectie - SetActivePage - stabileste pagina activa - SetAllPalette - schimba paletele de culoare - SetAspectRatio - seteaza factorul de corectie - SetBkColor - seteaza culoarea fondului - SetColor - seteaza culoarea cernelii - SetGraphBufSize - modifica dimensiunea bufferului grafic folosit la scanare sau umplere - SetGraphMode - seteaza sistemul la modul grafic specificat si sterge ecranul - SetPalette - schimba o culoare a unei palete cu alta - SetRGBPalette - seteaza paletele de culoare pt IBM8514 - setarea sau obtinerea punctului curent - GetX - intoarce abscisa punctului curent - GetY - intoarce ordonata punctului curent - MoveRel - muta punctul curent cu un deplasament precizat - MoveTo - schimba punctul curent la cel precizat - primitive grafice - Arc - traseaza un arc de cerc de centru si raza cunoscute, cuprins intre doua raze ce formeaza unghiuri date cu axa Ox - Bar - deseneaza o bara folosind stilul si culoarea de umplere curente - Bar3D - deseneaza o bara 3D folosind stilul si culoarea de umplere curente - Circle - traseaza un cerc de centru si raze cunoscute - Drawpoly - traseaza un poligon cu n varfuri folosind stilul de linie si culoarea curente - Ellipse - traseaza un arc elipsa data prin centru, semiaxa mare si mica si unghiurile razelor ce determina arcul cu semiaxa mare - FillEllipse - traseaza si umple o elipsa de centru si semiaxe cunoscute - FillPoly - traseaza un poligon si apoi il umple folosind stilul de umplere si culoarea curente - FloodFill - umple o regiune marginita cu sablonul de umplere si culoarea curenta - GetImage - salveaza o imagine (bitmap) dintr-o regiune specificata intr-un buffer (variabila) - PutImage - pune pe ecran o imagine dintr-un buffer - GetPixel - intoarce culoarea pixelului de la locatia (X,Y) specificata - PutPixel - schimba culoarea pixelului de la locatia specificata cu culoarea specificata - Line - traseaza o linie de la un punct la alt punct (ambele specificate); punctul curent nu se modifica - LineRel - traseaza o linie de la punctul curent (x0, y0) la punctul (x1, y1), unde: x1 = x0 + Dx y1 = y0 + Dy iar Dx si Dy sunt parametrii lui LineRel. (x1,y1) devinde noul punct curent

131

Page 132: Sub Program Are

- LineTo - traseaza o linie de la punctul curent (x0, y0) la punctul (x1, y1), unde x1 si y1 sunt parametrii lui LineTo. (x1,y1) devinde noul punct curent. - PieSlice - traseaza si umple o felie de placinta de centru, raza si unghiuri cunoscute - Rectangle - traseaza dreptunghiul definit de coltul dreapta sus si coltul stanga jos - Sector - traseaza si umple un sector de elipsa de parametri cunoscuti - parametri de umplere, sablon, stil - GetFillPattern - intoarce sablonul si culoarea de umplere selectate - GetFillSettings - intoarce sablonul si culoarea de umplere curente - GetLineSettings - intoarce setarile curente de stil, sablon si grosime de linie - SetFillPattern - selecteaza un sablon de umplere definit de utilizator - SetFillStyle - seteaza sablonul si culoarea de umplere - SetLineStyle - seteaza stilul, sablonul si grosimea liniei - informatii despre setarile curente - GetArcCoords - intoarce coordonatele ultimei comenzi Arc executate - GetAspectRatio - intoarce parametrii care determina factorul de corectie - GetBkColor - intoarce culoarea fondului - GetColor - intoarce culoarea cernelii - GetDefaultPalette - intoarce paleta de culoare cu care s-a initializat modul grafic - GetDriverName - intoarce un string continand numele driverului grafic - GetGraphMode - intoarce modul grafic curent - GetMaxColor - intoarce cel mai mare cod de culoare - GetMaxMode - intoarce cel mai mare numar de mod grafic - GetMaxX - intoarce rezolutia curenta pe orizontala - GetMaxY - intoarce rezolutia curenta pe verticala - GetModeName - intoarce numele modului grafic - GetModeRange - intoarce subdomeniul numerelor de moduri grafice valide pentru un anumit driver grafic - GetPalette - intoarce paleta curenta si dimensiunea ei - GetPaletteSize - intoarce dimensiunea paletei curente - GraphDefaults - seteaza pe (0,0) punctul curent si reseteaza parametrii grafici la valorile lor implicite - ImageSize - intoarce numarul de octeti necesar pentru a memora o regiune de ecran (bitmap) specificata - scrierea de text in modul grafic - GetTextSettings - intoarce setarile de text pentru scrierea in modul grafic - SetTextStyle - seteaza parametrii de stil de text - SetTextJustify - seteaza parametrii de aliniere a textului scris cu OutText sau OutTextXY in raport cu punctul curent - SetUserCharSize - seteaza latimea si inaltimea caracterelor din fontul curent - SetWriteMode - seteaza modul de desenare a liniilor (scrie peste, XOR, etc) - TextHeight - intoarce inaltimea stringului argument, in pixeli - TextWidth - intoarce latimea stringului argument, in pixeli - OutText - scrie stringul argument in mod grafic, in raport cu

132

Page 133: Sub Program Are

punctul curent - OutTextXY - scrie stringul argument in mod grafic, in raport cu un punct specificat - instalari de drivere, fonturi - InstallUserDriver - InstallUserFont - RegisterBGIdriver - RegisterBGIfont

8.2.3. Exemple de folosire

8.2.3.1. Initializarea modului grafic vezi UGraph.InitGr foloseste - DetectGraph - InitGraph - GraphResult - GraphErrorMsg

8.2.3.2. Programul Cercuri vezi Cercuri.PAS foloseste - UGraph.InitGr - CloseGraph - Circle

8.2.3.3. Programul Ferestre vezi Ferestre.PAS foloseste - UGraph.InitGr - CloseGraph - Circle - SetViewPort - Rectangle

8.3. Desenarea in plan a figurilor din planul real

Coordonate ecran - intregi definiti de rezolutia ecranului - sunt coordonate absolute - originea este coltul din stanga sus al ecranului (0, 0)Coordonate reale - coordonatele punctelor din planul real - se reprezinta prin numere realeCoordonate din fereastra fizica - sunt relative la coltul din stanga sus al ferestrei - se obtin din coordonatele ecran prin scaderea coordonatelor coltului stanga sus

Fereastra fizica ecran V (u1, v1) (u2, v2)

0 GetMaxX-1

133

Page 134: Sub Program Are

0ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ M1(u1, v1) ³ ³ v1ÅÄÄÄÄÄÄÄ ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ³ ³ º º ³ v ÅÄÄÄÄÄÄÄĶÄÄÄÄÄÄÄÄÄÄ.M(u, v) º ³ ³ º ³ º ³ v2ÅÄÄÄÄÄÄÄ ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ M2(u2, v2) ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÙGetMaxY-1 u1 u u2

- u1, v1 si u2, v2 sunt coordonate ecran - ordonata v creste in jos

Fereastra reala (logica): domeniul D din planul real D - definit de doua colturi opuse - (a, c) (b, d) - (a, d) (b, c) - ordonata y creste in sus

(a, d) x dÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ (b , d) ³ ³ ³ ³ ³ ³ ³ ³ ³ yÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ.P(x, y) ³ ³ ³ ³ ³ ³ ³ cÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ (b, c) a b

Punctul P(x, y) din D se reprezinta in punctul M(u, v) din V

Problema reprezentarii punctului P in coordonate ecran este: Date: domeniul real D (a, c) si (b, d) fereastra ecran (0, 0) si (GetMaxX-1, GetMaxY-1) punctul P(x, y) din D a <= x <= b, c <= y <= d Rezultate: (u, v)

Relatii de transformare

x - a u - u1 ----- = ------- u1 = 0, u2 = GetMaxX-1 b - a u2 - u1

y - d v - v1 ----- = ------- v1 = 0, v2 = GetMaxY-1 c - d v2 - v1

de unde rezulta

134

Page 135: Sub Program Are

u := Round((x-a)*(u2-u1)/(b-a)) + u1

v := Round((y-d)*(v2-v1)/(c-d)) + v1

u si v sunt coordonate ecran.

Daca se doreste reprezentarea numai in fereastra fizica V(caz in care coordonatele trebuie calculate relativ la coltul dinstanga sus al acesteia), atunci formulele de mai sus devin:

u' := Round((x-a)*(u2-u1)/(b-a))

v' := Round((y-d)*(v2-v1)/(c-d))

in care (u1, v1) si (u2, v2) reprezinta colturile ferestrei fizice

Pascal (limbaj de programare)De la Wikipedia, enciclopedia liberă

Salt la: Navigare, căutare

Pascal este unul dintre limbajele de programare de referinţă în ştiinţa calculatoarelor, fiind cel care a definit programarea calculatoarelor. Pascal a fost dezvoltat de elveţianul Niklaus Wirth în 1970 pentru a pune în practică programarea structurată, aceasta fiind mai uşor de compilat. Unul din marile sale avantaje este asemănarea cu limbajul natural limba engleză, ceea ce îl face limbajul ideal pentru cei care sunt la primul contact cu programarea. Pascal este bazat pe limbajul Algol şi a fost denumit astfel în onoarea matematicianului Blaise Pascal, creditat pentru construirea primelor maşini de calcul numeric. Wirth a mai dezvoltat limbajele Modula-2 şi Oberon, similare cu Pascal.

Cele mai populare implementări a acestui limbaj au fost Turbo Pascal şi Borland Pascal, ambele ale firmei Borland cu versiuni pentru Macintosh şi DOS, care i-au adăugat limbajului obiecte şi au fost continuate cu versiuni destinate programării vizuale pentru Microsoft Windows (limbajul Delphi) şi pentru Linux (Kylix).

În prezent există şi alte implementări mai mult sau mai puţin populare, dar gratuite, printre care se remarcă Free Pascal şi GNU Pascal.

Deşi în prezent este relativ rar folosit în industria software, el este încă util elevilor şi studenţilor care doresc să se iniţieze în programare. Spre deosebire de BASIC, care a stat în trecut la baza învăţării programării, Pascal este un limbaj structurat. De aceea, el formează un anumit tip de gândire, similar limbajelor moderne, precum C++, dar nu deţine complexitatea şi abstractizarea acestuia din urmă, fiind mai uşor de înţeles datorită sintaxei simple şi apropiate de pseudocod.

Cuprins

[ascunde]

135

Page 136: Sub Program Are

1 Tipuri de date de bază 2 Operatori (clasificare după tipul de date)

o 2.1 Pentru datele numerice (integer, real, byte, word) 2.1.1 Operatori relaţionali

o 2.2 Pentru datele de tip şir de caractere (string) 2.2.1 Operatori relaţionali

3 Instrucţiuni de bază o 3.1 Instrucţiune multiplă o 3.2 Condiţie o 3.3 Cicluri

3.3.1 Cu test iniţial 3.3.2 Cu test final 3.3.3 Cu număr cunoscut de paşi

4 Structura unui program Pascal 5 Câteva instrucţiuni

o 5.1 WRITE 5.1.1 Exemplu

o 5.2 READ 5.2.1 Exemple

o 5.3 READKEY 5.3.1 Exemplu

o 5.4 STR 5.4.1 Exemplu

o 5.5 VAL 5.5.1 Exemplu

o 5.6 BREAK o 5.7 EXIT o 5.8 HALT

5.8.1 Exemplu o 5.9 DELETE

5.9.1 Exemplu o 5.10 INSERT

5.10.1 Exemplu

[modifică] Tipuri de date de bază

Integer (numere întregi)

Real (numere reale)

Char (caractere)

String (şiruri de caractere)

Boolean (valori logice)

Text (fişiere text)

File (fişiere binare)

136

Page 137: Sub Program Are

Array (vectori)

[modifică] Operatori (clasificare după tipul de date)

[modifică] Pentru datele numerice (integer, real, byte, word)

  ( )   grupează expresiile

  +,-   adunare şi scădere

  *,/   înmulţire şi împărţire (împărţirea cu virgulă se face în numere reale)

  mod   returnează restul (doar în întregi)

  div   returnează câtul (doar în întregi)

[modifică] Operatori relaţionali

  <   mai mic

  >   mai mare

  =   egal

  <>   diferit

  <=   mai mic sau egal

  >=   mai mare sau egal

[modifică] Pentru datele de tip şir de caractere (string)

  +   concatenarea a două şiruri

137

Page 138: Sub Program Are

[modifică] Operatori relaţionali

  <,>   mai mare sau mai mic, pe baza ordinii lexicografice în funcţie de codurile ASCII

  =   cele două şiruri sunt identice

[modifică] Instrucţiuni de bază

[modifică] Instrucţiune multiplă

begin <instrucţiuni> end.

[modifică] Condiţie

if <condiţie> then <instrucţiune>;

sau

if <condiţie> then <instrucţiune> else <instrucţiune>;

[modifică] Cicluri

[modifică] Cu test iniţial

while <condiţie> do <instrucţiune>;

[modifică] Cu test final

repeat <instrucţiuni> until <condiţie>;

[modifică] Cu număr cunoscut de paşi

for <variabilă>:=<valoare_iniţială> to <valoare_finală> do <instrucţiune>;

sau

for <variabilă>:=<valoare_iniţială> downto <valoare_finală> do <instrucţiune>;

[modifică] Structura unui program Pascal138

Page 139: Sub Program Are

program <nume_program>;uses <biblioteci cu funcţii şi proceduri folosite în program>type <tipuri de date definite de utilizator>const <constante folosite în program>var <variabile globale folosite în program><aici se pot scrie funcţii şi proceduri folosite în program>begin <aici se scrie programul principal>end.

Notă: După fiecare comandă se pune " ; " cu următoarele excepţii: înainte de "else" şi după "do".

[modifică] Câteva instrucţiuni

[modifică] WRITE

Este o instrucţiune pentru afişarea pe ecran a unui text (şir de caractere) sau a valorii unor constante, unor variabile sau unor expresii, exceptând date de tip vector sau fişier. Conţinutul a ceea ce urmează să fie afişat pe ecran este încadrat de două paranteze rotunde. Lista a ceea ce urmează să fie afişat pe ecran este despărţită prin virgulă. Instrucţiunea WRITELN face aceeaşi acţiune ca şi WRITE dar după afişare mută cursorul la începutul rândului următor al ecranului.

[modifică] Exemplu

write ('a=', a);

Descriere: afişează pe ecran şirul a= după care afişează valoarea variabilei sau constantei a.

[modifică] READ

Este o instrucţiune care citeşte de la tastatură o variabilă, cu excepţia tipului boolean şi a vectorilor. Variabilele se scriu între paranteze şi, dacă sunt mai multe variabile, se despart prin virgulă. Instrucţiunea READLN face acelaşi lucru ca READ dar aşteaptă un <ENTER> înainte de a prelucra datele primite. În caz că sunt citite mai multe variabile, introducerea acestora se va face prin separarea valorilor prin spaţii albe (<Space>, <TAB> sau <ENTER>).

[modifică] Exemple

write ('Dati x='); readln (x);

Descriere: afişează pe ecran Dati x= după care citeşte de la tastatură valoarea variabilei x.

readln;

Descriere: aşteaptă apăsarea tastei <ENTER> după care continuă cu restul de instrucţiuni din program.

[modifică] READKEY

Este o instrucţiune de citire de la tastatură a caracterelor (valori de tip char) fără ca acestea să fie afişate pe ecran. Variabila caracter citită se scrie între paranteze.

[modifică] Exemplu

139

Page 140: Sub Program Are

write ('Parasiti aplicatia? (d/n)'); readkey (x);

Descriere: afişează pe ecran Parasiti aplicatia? apoi aşteaptă să fie introdusă de la tastatură o literă.

[modifică] STR

Transformă un număr într-un string (şir de caractere). Odată transformat în string, numărului nu i se mai pot efectua operaţii matematice deoarece acum este considerat un cuvânt.

[modifică] Exemplu

str (x, s);

Descriere: Creează un string din numărul x în şirul s.

[modifică] VAL

Transformă un string într-un număr atât timp cât stringul conţine o reprezentare validă.

[modifică] Exemplu

val (s, x, er);

Descriere: creează un număr din stringul s în variabila x. Dacă în timpul execuţiei se întâlneşte o eroare, variabila de tip integer er va conţine poziţia caracterului de la care s-a constatat că nu se poate transforma stringul în număr. Spre exemplu, pentru şirul de caractere ' 1234a6 ' variabila er va conţine 5.

[modifică] BREAK

Opreşte forţat un ciclu (FOR, REPEAT sau WHILE), chiar dacă acesta nu s-a încheiat.

[modifică] EXIT

Opreşte automat programul.

[modifică] HALT

Opreşte automat programul cu posibilitatea de transmite sistemului de operare un cod de eroare.

[modifică] Exemplu

halt (5);

Descriere: opreşte programul şi transmite codul de eroare 5.

[modifică] DELETE

Şterge o porţiune dintr-un string.

[modifică] Exemplu

140

Page 141: Sub Program Are

delete (s, 8, 4);

Descriere: şterge 4 caractere din şirul s începând cu poziţia 8.

[modifică] INSERT

Introduce un şir de caractere în altul.

[modifică] Exemplu

insert (s,'abc', 8);

Descriere: introduce în şirul s pe poziţia 8 caracterele abc.

141