Tipul enumerare

Ori de cāte ori scriem un program (Borland) PASCAL nu este bine sa ne limitam numai la tipurile ordinale primitive (predefinite) integer, char si boolean. (Borland) Pascal, spre deosebire de alte limbaje, ne ofera facilitatea de a crea tipuri ordinale suplimentare: enumerare (tratat īn aceasta sectiune) si subdomeniu. Ambele tipuri de date se utilizeaza īn special pentru a da mai multa claritate programelor.

Prin introducerea acestor tipuri de date ni se ofera posibilitatea de-a transmite compilatorului informatii īntr-un limbaj mai apropiat de limbajul nostru natural (informatii ce se supun unei sintaxe validate de compilator) si de a realiza astfel programe mai usor de scris si de citit.

Noile tipuri de date (enumerare si subdomeniu) pot fi identificate printr-un nume ales de utilizator (de regula īn concordanta cu semnificatia obiectelor de tipul respectiv), definit īntr-o sectiune TYPE, sau pot fi anonime, acestea definindu-se direct īn sectiunea VAR.

Iata un exemplu de sectiune TYPE care defineste tipuri enumerare, utilizate apoi īn sectiunea VAR pentru a declara o serie de variabile:

Exemplu:
PROGRAM...
CONST...
TYPE
zi=(luni, marti, miercuri, joi, vineri, sambata, duminica);
operator=(plus, minus, inmultire, impartire);
unitati=(mm, cm, dm, hm, m, km, dam);
legume=(cartof, morcov, rosie, ceapa);
vehicol=(automobil, tren, vapor, avion);
trufanda=(salata, rosii, vinete, ardei);
culoare=(alb, negru, rosu, verde, albastru);
functie=(decan, prodecan, rector, prorector);
ocupatie=(inginer, profesor, doctor, somer);
procesoare=(Intel, Motorola, Sparc);
VAR
trans:vehicul;
zilucru, zilibera:zi;
operatie:operator;
xum, yum:unitati;
cruditate:trufanda;
leg:legume;
persoana:functie;
...

Mai jos este prezentata diagrama de sintaxa a tipului enumerare:

Diagrama de sintaxa a tipului enumerare

Observati, deci, ca un tip enumerare este o lista de identificatori (Borland) PASCAL valizi, separati prin virgule si inclusi īntre paranteze rotunde.

Identificatorii din paranteze se numesc elementele tipului enumerare (īn engleza, elements of an enumerated type). Odata ce tipul (zi, operator,..., procesoare) a fost definit īn sectiunea TYPE, putem declara īn sectiunea VAR variabilele corespunzatoare ca fiind de tipul respectiv.

Putem atribui valori particulare variabilelor:

Exemplu:
....zilucru:=luni;
....zilibera:=duminica;
....operatie:=plus;
....xun:=mm;
....yun:=dm;
....trans:=vapor;

Nu putem scrie, de exemplu:

....persoana:=sefcatedra;

īntrucāt nu am prevazut īn descrierea tipului "functie" pe "sefcatedra" ca un nume de functie autorizat (acest identificator -sefcatedra- nu exsita īn lista de definire a tipului functie") cu toate ca putem sa o facem.

Din moment ce variabila zilucru, de exemplu, poate lua o data numai o singura valoare, atunci tipul corespunzator lui zi nu este numai tip de enumerare, ci este si un tip simplu de date. Nu este totusi un tip primitiv (standard sau predefinit) īntrucāt zilele saptamānii (luni, marti, miercuri, joi, vineri, sāmbata, duminica) reprezinta elemente ale unui tip enumerare, care nu pot fi citite sau scrise ca atare (chiar daca ele pot arata ca siruri de caractere, PASCAL-ul standard nu le considera astfel).

Exista dialecte PASCAL care permit introducerea si afisarea valorilor de tip enumerare, precum si a valorilor de tip boolean.

Pentru cazul mai sus prezentat:

succ(joi)=vineri

nu din cauza faptului ca (Borland) PASCAL-ul este destept, ci numai pentru faptul ca zilele saptamānii au fost listate īn ordinea lor obisnuita atunci cānd am declarat tipul zi. Aceasta ordine aleasa de noi, īn mod natural, fireste, forteaza ca ord(luni) sa fie zero si ord(duminica) sa fie 6. Totusi succ(duminica) si pred(luni) sunt nedefinite chiar daca pentru noi toti este evident ca dupa duminica urmeaza luni.

Trebuie sa facem cāteva remarci suplimentare referitoare la tipul enumerare.

Remarci:
  • daca īntr-un program PASCAL gasim ca succ(joi) este sāmbata, cauza poate fi lipsa identificatorului "vineri" din declaratia (originala) a tipului de date care enumera numele zilelor (zi). Nici o actiune procedurala nu poate plasa "vineri" la locul cuvenit īn cadrul listei cu numele zilelor saptamānii;
  • daca, īn cunoscutul film "Daca e marti e Belgia", fiecare din zilele saptamānii reprezenta o tara europena (exemplu: marti, Belgia) īn (Borland) PASCAL, enunturi ca:
luni:='Franta';
marti:='Belgia';
-
-
-
duminica:='Italia';

nu sunt legale daca luni,..., duminica reprezinta elementele unui tip enumerare. Īn astfel de cazuri luni, marti etc. sunt identificatori, reprezentāndu-se pe ei īnsisi - nu sunt variabile.

Acum, referitor la omisiunea lui vineri, sa presupunem ca un programator este ispitit sa declare doua tipuri enumerare īn aceasi sectiune type, ca īn exemplul urmator:

Exemplu:
...
TYPE
zi=(luni, marti, miercuri, joi, vineri, sambata, duminica);
zile=(luni, marti, miercuri, joi, vineri, sambata, duminica);
...

Nu este nimic gresit īn legatura cu tipul celor sase zile, dar exista o problema cu duplicarea celor sase nume īn tipurile zi si zile. Ce poate raspunde compilatorul (Borland) PASCAL la succ(joi)? Urmatoarea regula previne astfel de situatii.

Regula:

Elementele tipurilor enumerare declarate īn sectiunile type trebuie sa fie unice.

Exprimarea "īn sectiunile TYPE" acopera situatia īn care un program principal si procedurile sale au fiecare propriile lor sectiuni TYPE. Asa cum īntr-o procedura se pot declara variabile locale care au acelasi nume ca variabilele (globale) din programul principal, tot asa se pot folosi elemente ale unui tip enumerare, locale, care au acelasi nume cu identificatorii din programul principal.

Remarca:

Declaratiile anonime ale tipului enumerare, din sectiunea VAR pot genera o serie de probleme de compatibilitate; de aceea se recomanda ca orice nou tip de data necesar īn rezolvarea unei probleme sa fie definit explicit (īn sectiunea TYPE).

Precizam la īnceputul sectiunii cāteva din avantajele pe care le ofera tipul enumerare. Īn cele ce urmeaza ne propunem sa fim mai convingatori.

Daca o problema ce implica zilele saptamānii o putem rezolva apelānd la tipul enumerare, la fel de bine putem rezolva aceeasi problema folosind, pentru zilele saptamānii coduri, de la 1 la 7 sau de la 0 la 6. Folosirea unei liste de tip enumerare, cu specificatii literale ale zilelor saptamānii are doua avantaje. Primul: daca am folosi numere (coduri), putem savārsi erori de genul - ziua a 8-a sau a 13-a - exprimari legale sintactic, dar semantic fara sens. Cu exprimari literale (text), compilatorul nu va avea de ce sa se ... blocheze.

Al doilea avantaj: tipul de date enumerare confera programului mai multa claritate.

Enuntul:

VAR z:zi;
...
FOR z:=luni TO duminica DO
writeln(...);

este cu siguranta mult mai pe īntelesul cititorului, decāt corespondentul sau:

FOR z:=0 TO 6 DO
writeln(...);

Folosirea judicioasa īn cadrul programelor PASCAL a tipului enumerare duce la minimizarea comentariilor, programele devenind mult mai clare si mai usor de īnteles. Si acum, o aplicatie care foloseste tipul de date enumerare.

Aplicatie. Dāndu-se un numar de la 1 la 365 , care reprezinta ziua din cadrul unui an (nu un an bisect) sa se afiseze īn clar data respectiva. De exemplu, pentru 1 se va afisa 1 ianuarie; pentru 365 se va tipari 31 decembrie s.a.m.d. Se va tipari un mesaj de eroare daca numarul introdus este mai mic ca 1 si mai mare ca 365.
Indicatie. Daca numarul precizat se afla īn intervalul 1-31, inclusiv , evident luna este ianuarie. Pentru domeniul 32-59 luna trebuie sa fie februarie. Daca numarul introdus este 40, data este: (40 minus 31) februarie, unde 31 reprezinta numarul de zile ale lunii anterioare. Asadar, pentru numarul 40 programul va afisa 9 februarie.

Lista programului sursa si rezultatele executiei programului sunt prezentate īn continuare:

PROGRAM enumerare;
{programul afiseaza in clar ziua si luna calendaristica}
TYPE
luna=(ianuarie, februarie, martie, aprilie, mai, iunie, iulie, august, septembrie, octombrie, noiembrie, decembrie);
VAR
nrzi:integer; {variabila de intrare: numarul zilei}
m:luna; {numele lunii corespunzatoare numarului zilei}
zi:integer; {numarul zilei īn cadrul lunii identificate}
PROCEDURE printzi(m:luna);
{Afiseaza luna corespunzatoare lui m}
BEGIN
...IF m = ianuarie THEN write('ianuarie') ELSE
...IF m = februarie THEN write('februarie') ELSE
...IF m = martie THEN write('martie') ELSE
...IF m = aprilie THEN write('aprilie') ELSE
...IF m = mai THEN write('mai') ELSE
...IF m = iunie THEN write('iunie') ELSE
...IF m = iulie THEN write('iulie') ELSE
...IF m = august THEN write('august') ELSE
...IF m = septembrie THEN write('septembrie') ELSE
...IF m = octombrie THEN write('octombrie') ELSE
...IF m = noiembrie THEN write('noiembrie') ELSE
...IF m = decembrie then write('decembrie')
END {printzi};
BEGIN
WHILE NOT EOF DO
.BEGIN
...write('Introduceti un numar īntre 1 si 365: ');
...readln(nrzi);
...IF (nrzi <1) OR (nrzi>365) THEN
....writeln('numarul nu se afla īn intervalul 1-365')
...ELSE
...BEGIN
....IF nrzi<32 THEN
....BEGIN
.....m:=ianuarie;zi:=nrzi END ELSE
.....IF nrzi<60 THEN
.....BEGIN
......m:=februarie;zi:=nrzi-31 END ELSE
......IF nrzi<91 THEN
......BEGIN
.......m:=martie;zi:=nrzi-59 END ELSE
.......IF nrzi<121 THEN
........BEGIN
.........m:=aprilie;zi:=nrzi-90 END ELSE
.........IF nrzi<152 THEN
.........BEGIN
..........m:=mai;zi:=nrzi-120 ELSE END
..........IF nrzi<182 THEN
..........BEGIN
...........m:=iunie;zi:=nrzi-151 END ELSE
...........IF nrzi<213 THEN
...........BEGIN
............m:=iulie;zi:=nrzi-181 END ELSE
............IF nrzi<244 THEN
............BEGIN
.............m:=august;zi:=nrzi-212 END ELSE
.............IF nrzi<274 THEN
.............BEGIN
..............m:=septembrie;zi:=nrzi-243 END ELSE
..............IF nrzi<305 THEN
..............BEGIN
...............m:=octombrie;zi:=nrzi-273 END ELSE
...............IF nrzi<335 THEN
...............BEGIN
................m:=noiembrie;zi:=nrzi-304 END ELSE
................BEGIN
.................m:=decembrie;zi:=nrzi-334
................END;
... write('ziua nr. ',nrzi:1,' este');
....printzi(m);
....writeln(' ',zi:1)
...END;
...writeln
.END
END {enumerare}.

Rezultatele executiei programului

Introduceti un numar intre 1 si 365: 1
ziua nr. 1 este ianuarie 1
Introduceti un numar intre 1 si 365: 365
ziua nr. 365 este decembrie 31
Introduceti un numar intre 1 si 365: 500
numarul nu se afla in intervalul 1-365
Introduceti un numar intre 1 si 365: ^Z

Remarca.

^Z (Ctrl + Z) este end of file.