Documente Academic
Documente Profesional
Documente Cultură
Program Area Calculatoarelor Limbajul PASCAL
Program Area Calculatoarelor Limbajul PASCAL
3
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
1.1.
VAR a,b,c:REAL; {Se rezervă câte 6 octeţi pentru fiecare variabilă}
pa,pb:^REAL; {Se rezervă câte 4 octeţi pentru fiecare variabilă}
BEGIN
a:=20; {Se atribuie valoarea 20 variabilei de adresa a} 1
b:=a; {Se atribuie variabilei de adresa b, conţinutul variabilei
de adresa a} 2
pa:=@a; {Se atribuie variabilei de adresa pa, adresa a} 3
4
Tipuri dinamice de date. Lucrul cu adrese
5
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
6
Tipuri dinamice de date. Lucrul cu adrese
7
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
instrucţiuni cod maşină conform tipului de bază, dar cu adresare indirectă. Înainte de
referire, în variabilele de tip_referinţă trebuie să se încarce adresele variabilelor de tipul
tip_de_bază. Declararea unui tip referinţă permite referirea anterior declarării tipului de
bază. Astfel, următoarea secvenţă de declarări este corectă: TYPE pointer_a=^vector;
vector=ARRAY[1..20] OF REAL;
Construcţia sintactică a referirii unei variabile dinamice depinde de
caracteristicile tipului său de bază: este de forma identificator^ în cazul tipurilor
nestructurate sau celor structurate care permit referirea globală (STRING, RECORD şi
SET); conţine prefixul identificator^, urmat de elementele specifice modului de
referire a componentelor, în cazul tipurilor structurate care permit referirea pe
componente (ARRAY, STRING şi RECORD). Aceste posibilităţi de referire sunt
ilustrate în programul Alocare_dinamică_1.
• Tipul pointer este desemnat prin cuvântul rezervat pointer. Variabilele de tip
pointer pot fi denumite variabile cu referinţă liberă, deoarece pot fi folosite la
memorarea adreselor pentru variabile de orice tip. Tehnica de lucru cu astfel de variabile
este asemănătoare celei prezentate la tipul referinţă. Utilizarea efectivă presupune şi în
acest caz o asociere explicită cu un anumit tip de bază, dar soluţia folosită este diferită.
La tipul referinţă, asocierea se face prin declarare, iar în cazul tipului pointer asocierea
se realizează la utilizare, prin diverse tehnici. O posibilitate este asigurată de referinţa
typecasting (transfer de tip), care are forma generală: tip(variabilă), unde tip este un tip
standard sau declarat anterior de utilizator iar variabilă poate fi cu/fără tip sau o
referinţă prin pointer, de forma variabilă_pointer^.
Din punct de vedere fizic, variabilele de tip referinţă_legată şi pointer
memorează adrese sub forma segment:offset. De aceea, în limbajul curent de
specialitate, ambele tipuri se definesc prin termenul pointer. Următorul exemplu
evaluează expresia e:=a+b, folosind adresarea indirectă pentru toate variabilele (a şi e
prin referinţă_cu_tip iar b prin pointer):
VAR
a,b,e:REAL;
pa,pe:^REAL;
pb:POINTER;
BEGIN
pa:=addr(a); pb:=@b; pe:=@e;
Write(‘A=); ReadLn(pa^);
Write(‘B=’); ReadLn(REAL(pb^));
pe^:=pa^+REAL(pb^);
WriteLn(‘E= ‘,pe^:8:2))
END.
8
Tipuri dinamice de date. Lucrul cu adrese
9
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
10
Tipuri dinamice de date. Lucrul cu adrese
11
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
PROGRAM Alocare_dinamica_2;
leg=^element;
element=RECORD
v:INTEGER;
urm:leg;
END;
VAR
inceput,curent,urmator: leg;
n,vi,i:INTEGER;
BEGIN
Write('Nr. de elemente si valoare de inceput: ');
ReadLn(n,vi);
New(inceput);
inceput^.v:=vi;
inceput^.urm:=nil;
WriteLn('S-a creat elementul 1 = ',inceput^.v);
curent:=inceput;
FOR i:=2 TO n DO
BEGIN
New(urmator);
urmator^.v:=curent^.v+1;
urmator^.urm:=nil;
WriteLn('S-a creat elementul ',i,' = ',urmator^.v);
curent^.urm:=urmator;
curent:=urmator
END;
i:=1;
curent:=inceput;
WHILE curent^.urm <> nil DO
BEGIN
WriteLn('v[',i,']: ',curent^.v);
curent:=curent^.urm;
Inc(i)
END;
Writeln('v[',i,']: ',curent^.v)
END.
PROGRAM Alocare_dinamica_3;
12
Tipuri dinamice de date. Lucrul cu adrese
VAR
p: POINTER;
BEGIN
GetMem(p,6);
REAL(p^):=5.25;
WriteLn(REAL(p^):4:2);
FreeMem(p,6)
END.
PROGRAM Alocare_dinamica_4;
TYPE
element=RECORD
v:INTEGER; urm:POINTER;
END;
VAR
inceput,curent,urmator: POINTER;
n,vi,i : INTEGER;
BEGIN
Write('Nr. de elemente si valoare de inceput: ');
ReadLn(n,vi);
GetMem(inceput,6);
element(inceput^).v:=vi;
element(inceput^).urm:=nil;
WriteLn('S-a creat elementul 1 = ',element(inceput^).v);
curent:=inceput;
FOR i:=2 TO n DO
BEGIN
GetMem(urmator,6);
element(urmator^).v:=element(curent^).v+1;
element(urmator^).urm:=nil;
WriteLn('S-a creat elementul ',i,' =
'element(urmator^).v);
element(curent^).urm:=urmator;
curent:=urmator
END;
i:=1;
curent:=inceput;
WHILE element(curent^).urm <> nil DO
BEGIN
WriteLn('v[',i,']: ',element(curent^).v);
curent:=element(curent^).urm;
Inc(i)
END;
Writeln('v[',i,']: ',element(curent^).v)
END.
13
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
VAR
x1,x2,x3.x4:^REAL;
y:POINTER;
BEGIN
New(x1); x1^:12;
Mark(y); {Memorarea valorii HeapPtr in Y}
New(x2); x2^:=10;
New(x3); x3^:=34;
Release(y); {Reincarcarea valorii din Y in Heapptr}
New(x4); x4:=46; {Se va memora peste valoare 10 din x2}
………………………
14
STRUCTURA DE LISTĂ
ÎN LIMBAJUL PASCAL
Unul din principalele atribute ale datelor este structura sau modul de
organizare care le caracterizează. Structurile rezultă prin gruparea într-un anumit
mod a colecţiilor de date primitive. Exemple de structuri de date sunt: tablourile,
mulţimile, înregistrările. În continuare vor fi prezentate noi tipuri de organizări, şi
anume: listele simplu şi dublu înlănţuite şi cazurile particulare cunoscute sub numele
de stive şi cozi.
15
Programarea calculatoarelor - Tehnica programării în limbajul Pascal
16
Structura de listă în limbajul Pascal
dn dn-1 … d2 d1
C a)
dn dn-1 … d2 d1
b)
17
Programarea calculatoarelor - Tehnica programării în limbajul Pascal
dn dn-1 … d2 d1
a) ultim
dn dn-1 … d2 d1
ultim
b)
Accesul la informaţia stocată într-o variabilă de tip listă revine efectuând una
sau mai multe dintre operaţiile primitive: regăsirea nodului (dacă există) care
corespunde unei chei date (condiţie impusă asupra valorii câmpului de informaţie),
inserarea unei noi componente în listă, eliminarea componentei (componentelor) cu
proprietatea că valorile câmpurilor de informaţie satisfac o anumită cerinţă şi
înlocuirea câmpului de informaţie corespunzător unei componente printr-o informaţie
dată.
Accesarea componentelor unei liste reprezentată printr-o structură statică
poate fi realizată atât secvenţial, cât şi direct, utilizând valorile indicelui considerat
pentru indexare, în timp ce accesarea componentelor unei liste dinamice se
realizează, de regulă, numai secvenţial, începând cu prima componentă şi continuând
cu următoarele, pe baza valorilor câmpurilor de legătură. Convenţional, numim cap
al listei dinamice pointerul a cărui valoare este adresa primei componente a listei.
18
Structura de listă în limbajul Pascal
function exista2(cap:lista;d:tip_informatie):boolean;
var p:lista;
19
Programarea calculatoarelor - Tehnica programării în limbajul Pascal
c:boolean;
begin
p:=cap;
c:=false;
while(p<>nil)and(not c) do
if egal(d,p^.inf) then c:=true
else p:=p^.leg;
exista2:=c;
end;
function exista3(cap:lista;d:tip_informatie):boolean;
var p:lista;
c:boolean;
begin
p:=cap;
c:=false;
while(p<>nil)and(not c) do
if egal(d,p^.inf) then c:=true
else p:=p^.dr;
exista3:=c;
end;
20
Structura de listă în limbajul Pascal
var i:word;
begin
test:=true;
if n=max then test:=false
else
begin
for i:=n downto 1 do atribuie(l[i+1],l[i]);
atribuie(l[1],d);
n:=n+1;
end;
end;
procedure inserare_la_inceput2(var
cap:lista;d:tip_informatie;var test:boolean);
var p:lista;
begin
test:=true;
if MaxAvail<sizeof(nod) then test:=false
else
begin
new(p);
atribuie(p^.inf,d);
p^.leg:=cap;
cap:=p;
end;
end;
procedure inserare_la_inceput3(var
cap:lista;d:tip_informatie;var test:boolean);
var p:lista;
begin
test:=true;
if MaxAvail<sizeof(nod) then test:=false
else
begin
new(p);
atribuie(p^.inf,d);
p^.dr:=cap;p^.st:=nil;
cap:=p;
end;
end;
21
Programarea calculatoarelor - Tehnica programării în limbajul Pascal
d:tip_informatie;
begin
test:=true;
if n>max then test:=false
else
begin
i:=1;
while(i<=n) do
begin
copiaza(d,i);
inserare_la_inceput1(l,i,d,test)
end;
end;
end;
22
Structura de listă în limbajul Pascal
type
tip_informatie=string;
clista=^nod;
nod=record
inf:tip_informatie;
leg:clista;
end;
23
Programarea calculatoarelor - Tehnica programării în limbajul Pascal
24
Structura de listă în limbajul Pascal
begin
test:=true;
if ultim=nil then test:=false
else
begin
cap:=ultim^.leg;
atribuie(d,cap^.inf);
if cap=ultim then
begin
dispose(ultim);
ultim:=nil;
end
else
begin
ultim^.leg:=cap^.leg;
dispose(cap);
end;
end;
end;
procedure elimina_la_sfarsit(var ultim:clista;var
d:tip_informatie;var test:boolean);
var
p:clista;
cap:clista;
begin
test:=true;
if ultim=nil then test:=false
else
begin
cap:=ultim^.leg;
atribuie(d,ultim^.inf);
if cap=ultim then
begin
dispose(ultim);
ultim:=nil;
end
else
begin
p:=cap;
while(p^.leg<>ultim) do p:=p^.leg;
p^.leg:=cap;
dispose(ultim);
ultim:=p
end;
end;
end;
procedure afisare(ultim:clista);
var p,cap:clista;
begin
if ultim=nil then writeln('Lista vida')
else
begin
cap:=ultim^.leg;
p:=cap;
repeat
write(p^.inf,' ');
p:=p^.leg;
until p=cap;
writeln;
end;
end;
25
Programarea calculatoarelor - Tehnica programării în limbajul Pascal
Coada
Se numeşte coadă o listă organizată astfel încât operaţia de inserare este
permisă la ultima componentă, iar operaţia de eliminare este permisă numai la prima
componentă. Acest mod de organizare corespunde unei gestiuni FIFO (First In First
Out) a informaţiei stocate.
Modelul corespunde unei cozi de aşteptare la un magazin. O nouă persoană
se aşază la coadă după ultimul cumpărător, iar persoana care îşi achită nota de plată
(primul cumpărător) părăseşte coada.
Implementarea unei liste coadă poate fi efectuată atât printr-o structură
statică (masiv unidimensional), cât şi printr-o structură dinamică de tip listă. În
scopul eficientizării operaţiilor de inserare/extragere, în cazul implementării cozilor
prin structuri dinamice lineare, este necesară utilizarea a două informaţii: adresa
26
Structura de listă în limbajul Pascal
27
GRAFURI. IMPLEMENTĂRI
ÎN LIMBAJUL PASCAL
Definiţia 3.1.3. Graful G = (V,E) este finit, dacă V este o mulţime finită.
28
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Cea mai simplă reprezentare a unui graf este cea intuitivă, grafică; fiecare
vârf este figurat printr-un punct, iar muchiile sunt reprezentate prin segmentele de
dreaptă, orientate (în cazul digrafurilor) sau nu şi etichetate (în cazul grafurilor
ponderate) sau nu, având ca extremităţi punctele corespunzătoare vârfurilor care le
determină.
Exemple:
3.1. Fie G = (V, E) graf, cu V = {1, 2, 3, 4, 5}, E = {(1,2),(1,3),(2,5),(3,5)}.
O posibilă reprezentare grafică este:
2 ● 4
3
5
29
Grafuri. Implementări în limbajul Pascal
2 ● 4
3
5
5 1
2 3
7
2
4
Deşi acest mod de reprezentare este foarte comod şi sugestiv, în special în
cazul grafurilor cu număr mic de vârfuri, pentru prelucrări cu ajutorul calculatorului
sunt necesare reprezentări prin intermediul structurilor de date.
O modalitate de reprezentare este cea prin matrice de adiacenţă. Dacă
G=(V,E) este graf sau digraf cu V = n , atunci matricea de adiacenţă A ∈ Mnxn({0,1})
are componentele:
( )
1, dacă vi , v j ∈ E
a ij = ,
0, altfel
30
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
unde vi, vj reprezintă cel de-al i-lea, respectiv cel de-al j-lea nod din V. Se observă că,
în cazul unui graf neorientat, matricea de adiacenţă este simetrică -
∀i, j = 1, n , a ij = a ji (perechile de vârfuri ce caracterizează muchiile sunt
neordonate, deci dacă uv ∈ E, atunci şi vu ∈ E), în timp ce, în cazul unui digraf, este
( ) ( )
posibil ca v i , v j ∈ E, v i , v j ∉ E , deci aij ≠ aji.
Exemplu:
3.4. Graful din exemplul 3.1 şi digraful din exemplul 3.2 sunt reprezentate
prin matricele de adiacenţă:
0 1 1 0 0 0 1 1 0 1
1 0 0 0 1 0 0 0 0 1
A = 1 0 0 0 1 pentru 3.1, A = 0 0 0 0 1 pentru 3.2
0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 0 0 0
Exemplu:
3.5. Presupunând că ponderile reprezintă costuri, matricea de reprezentare a
grafului din exemplul 3.3. este:
∞ 5 1 7
5 ∞ ∞ 2
W= .
1 ∞ ∞ ∞
7 2 ∞ ∞
Reţinând numai “informaţia utilă”, şi anume existenţa unei muchii între două
vârfuri şi eventual valoarea ponderii ei, se obţine reprezentarea tabelară, mai
economică din punctul de vedere al spaţiului de memorare. În cazul în care există
vârfuri izolate în graf (ce nu sunt incidente cu nici o muchie), atunci este necesară
păstrarea acestora într-un vector suplimentar. Mulţimea muchiilor se memorează
31
Grafuri. Implementări în limbajul Pascal
32
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemple:
3.8 Fie graful:
2 3
4 6
5 7
şi v0=1.
33
Grafuri. Implementări în limbajul Pascal
vârf 1 2 3 4 5 6 7
m
0 0 -1 -1 -1 -1 -1 -1
1 0 1 1 -1 1 -1 1
2 0 1 1 2 1 2 1
0 1 1 2 1 2 1
Ordinea de vizitare a vârfurilor: 1,2,3,5,7,4,6.
1 8
2 3
4 6 9 10
7
5
şi v0=1.
Se observă că vârfurile 8, 9 şi 10 nu sunt conectate cu vârful iniţial.
Valorile rezultate prin aplicarea metodei sunt:
vârf 1 2 3 4 5 6 7 8 9 10
m
0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1
1 0 1 1 -1 1 -1 1 -1 -1 -1
2 0 1 1 2 1 2 1 -1 -1 -1
0 1 1 2 1 2 1 -1 -1 -1
Ordinea de vizitare a vârfurilor este 1,2,3,5,7,4,6.
34
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
uses crt;
type
ptcoada=^coada;
coada=record
inf:byte;
leg:ptcoada;
end;
var
prim,ultim:ptcoada;
c:array[1..20] of byte;
a:array[1..20,1..20] of byte;
i,j,k,n:byte;
procedure push(var p,u:ptcoada;i:byte);
var a:ptcoada;
begin
new(a); a^.inf:=i; a^.leg:=nil;
if p=nil then
begin
p:=a; u:=a;
end
else begin
u^.leg:=a;u:=a;
end;
end;
procedure pop(var p,u:ptcoada;var i:byte);
var a:ptcoada;
begin
if p<>nil then
begin
i:=p^.inf; a:=p; p:=p^.leg;
dispose(a);
if p=nil then u:=nil;
end;
35
Grafuri. Implementări în limbajul Pascal
end;
begin {program principal}
clrscr;
write('Numarul de varfuri:');
readln(n);
writeln('Matricea de adiacenta');
for i:=1 to n do
for j:=1 to n do
begin
write('a[',i,',',j,']=');
readln(a[i,j]);
end;
for i:=2 to n do c[i]:=0;
readln;
clrscr;
writeln('Incepem parcurgerea de la nodul 1');
c[1]:=1;
prim:=nil;ultim:=nil;
push(prim,ultim,1);
while prim<>nil do
begin
pop(prim,ultim,i);
write(i,' ');
for k:=1 to n do
if (a[i,k]=1)and(c[k]=0) then
begin
c[k]:=1;
push(prim,ultim,k);
end;
end;
readln;
end.
36
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemple:
3.10. Pentru graful:
2 3
4 6
5 7
1 8
2 3
4 6 9 10
7
5
37
Grafuri. Implementări în limbajul Pascal
este efectuat un nou acces de preluare a noului vârf al stivei ca vârf curent. Calculul
se încheie în momentul în care este efectuat un acces de preluare a vârfului stivei ca
vârf curent şi se constată că S este vidă. Evident, nici în cazul acestei variante nu vor
fi vizitate vârfurile care nu sunt conectate cu vârful ales iniţial.
Definiţia 3.3.3. Fie Γ: u0, u1,..,un un drum în graful G = (V,E). Γ’: v0, v1,..,vm
este un subdrum al lui Γ dacă Γ’ este un drum şi pentru orice j, 0 ≤ j ≤ m , există i,
0 ≤ i ≤ n , astfel încât ui = vj.
Evident, orice drum cu lungime cel puţin 1 conţine cel puţin un drum
elementar cu aceleaşi extremităţi.
Într-adevăr, dacă Γ: u0, u1,..,un nu este elementar, atunci există
0 ≤ i < j ≤ n şi i ≠ 0 sau j ≠ n, astfel încât ui = uj.
Atunci drumul
38
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
grafului, atunci, pentru orice p≥1, a ij( p ) este numărul vi-vj drumurilor distincte de
39
Grafuri. Implementări în limbajul Pascal
Exemplu:
3.12. Pentru graful:
0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1
1 0 0 0 2 0 1 1 1 3 1 0 1 1 1 1 1 1
A= ,A = ,A = ,M =
1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1
1 0 1 0 1 1 1 1 1 1
1 1 1 1 1 1
Calculul matricei existenţei drumurilor permite verificarea faptului că un
graf dat este conex: graful este conex dacă şi numai dacă toate componentele
matricei M sunt egale cu 1.
Algoritmul Roy-Warshall calculează matricea existenţei drumurilor într-un
graf G cu n vârfuri.
procedure Roy_Warshall (a,n,m);
i,j,k:integer;
do-for i=1,n,1
do-for j=1,n,1
mij=aij;
do-for j=1,n,1
do-for i=1,n,1
if mij =1 then
do-for k=1,n,1
if mik<mkj then
mik=mkj;
endif;
enddo;
endif;
enddo;
enddo;
end;
40
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Definiţia 3.3.5. Fie G=(V,E) graf netrivial. Vârfurile u,v ∈ V sunt conectate
dacă există un u-v drum în G.
Definiţia 3.3.6. Dacă G este un graf, atunci o componentă conexă a lui G este
un subgraf conex al lui G, maximal în raport cu proprietatea de conexitate.
Evident, un graf este conex dacă şi numai dacă numărul componentelor sale
conexe este 1. Mulţimile vârfurilor corespunzătoare oricăror două componente
conexe distincte sunt disjuncte. Mulţimile vârfurilor corespunzătoare componentelor
conexe ale unui graf formează o partiţie a mulţimii vârfurilor grafului.
Multe aplicaţii modelate în termeni de grafuri impun determinarea
componentelor conexe corespunzătoare unui graf dat. Problema poate fi rezolvată în
modul următor: se selectează un vârf al grafului, se determină componenta conexă
care-l conţine; dacă există vârfuri care nu aparţin componentei conexe determinate,
se alege unul dintre acele vârfuri căruia i se determină componenta conexă care-l
conţine; în continuare, se repetă procedeul până când au fost găsite toate
componentele conexe ale grafului.
Pentru G=(V,E), V = n , n ≥ 1 şi v0 ∈ V, paşii algoritmului pentru
determinarea componentei conexe care conţine un vârf v0 dat sunt:
Ieşirea este G1=(Vi,Ei), componenta conexă din care face parte v0.
Exemplu:
3.13. Pentru graful
1 2 7 3
4 5 8 6
41
Grafuri. Implementări în limbajul Pascal
i Vi Ei
i=0 {1} Ø
i=1 {1,2,4} {(1,2),(1,4)}
i=2 {1,2,4,7,5} {(1,2),(1,4),(2,7),(4,5),(4,7)}
i=3 {1,2,4,7,5,8} {(1,2),(1,4),(2,7),(4,5),(4,7),(5,8),(7,8)}
i=4 {1,2,4,7,5,8} {(1,2),(1,4),(2,7),(4,5),(4,7),(5,8),(7,8)}
Algoritmul Dijkstra
Algoritmul a fost propus de E. W. Dijkstra pentru determinarea w-distanţelor
D(u0,v) şi a câte unui u0-v drum de cost minim pentru fiecare vârf v≠u0 într-un graf
ponderat, unde u0 este prestabilit.
Fie (V,E,w) graf conex ponderat, u0∈V, S⊂V, u0∈S. Se notează S = V \ S şi
( ) { }
D u 0 , S = min D(u 0 , x ); x ∈ S . Fie v∈ S astfel încât D(u0,v)=D(u0, S ), Γ : u0,
u1,…,upv este un u0-v drum de cost minim. Evident, ∀0≤i≤p ui∈S şi Γ ’: u0, u1,…,up,
un u0- up drum de cost minim. De asemenea,
( ) {
D u 0 , S = min D(u 0 , u ) + w (uv); u ∈ S, v ∈ S, uv ∈ E . }
42
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Evident, dacă (V,E,w) este graf ponderat neconex, atunci, pentru u0∈V,
algoritmul lui Dijkstra permite determinarea w-distanţelor D(u0,v) şi a câte unui u0-v
drum de cost minim pentru toate vârfurile v din componenta conexă căreia îi aparţine
u0.
Exemplu:
5 1
9
2 3
2 16
5
5
4
43
Grafuri. Implementări în limbajul Pascal
44
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
uses crt;
var a:array[1..200,1..3] of integer;
s,dr,t,l:array[1..20] of integer;
i,j,k,p,v1,u,c,d:integer;
m,v:integer;
begin
clrscr;
readln(v);
readln(m);
for i:=1 to m do
for j:=1 to 3 do
begin
write('a[',i,',',j,']=');
readln(a[i,j]);
end;
i:=1;s[1]:=1;v1:=1;
for j:=2 to v do
begin
t[j-1]:=j; l[j]:=maxint;
end;
l[1]:=0;
for k:=1 to v-1 do
begin
for j:=1 to v-i do
begin
u:=1;
while((t[j]<>a[u,1])or(v1<>a[u,2]))and
((t[j]<>a[u,2])or(v1<>a[u,1]))and(u<=m)
do inc(u);
if(u<=m) and (l[t[j]]>l[v1]+a[u,3]) then
begin
l[t[j]]:=l[v1]+a[u,3];
dr[t[j]]:=v1;
end;
end;
d:=l[t[1]];v1:=t[1];
for j:=2 to v-i do
if l[t[j]]<d then begin
d:=l[t[j]];
45
Grafuri. Implementări în limbajul Pascal
v1:=t[j];
end;
inc(i);
s[i]:=v1;
u:=1;
while(u<=v-i+1)and(t[u]<>v1) do inc(u);
for j:=u to v-i do t[j]:=t[j+1];
end;
clrscr;
for i:=1 to v do
writeln(i,'--->',l[i]);
writeln('Drumurile minime de la fiecare varf la 1:');
for j:=2 to v do
begin
k:=j;write(k,' ');
while k<>1 do
begin
write(dr[k],' '); k:=dr[k];
end;
writeln;
end;
readln;
end.
Algoritmul Roy-Floyd
46
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
end;
Algoritmul Yen
Pasul 1: D = W
Pasul 2: i = 1; λ(k) = 0, b(k) = 0; λ(j)=0, pentru toţi 1 ≤ j ≤ n , j ≠ k
Pasul 3: Calculează min{dkj; 1 ≤ j ≤ n , λ(j)=1}; determină j0 astfel încât
λ(j0) = 1 şi d kj0 = min{dkj; 1 ≤ j ≤ n , λ(j) = 1}
B(j0) = d kj0 , λ(j0) = 0
d[k,j] = min{d[k,j],d[k,j0]+d[j0,j]}, pentru toţi j, 1 ≤ j ≤ n
i=i+1
Pasul 4: Dacă i<n, reia Pasul 3, altfel stop.
47
STRUCTURI ARBORESCENTE
În clasa grafurilor conexe, structurile cele mai simple, dar care apar cel mai
frecvent în aplicaţii, sunt cele arborescente (arbori). În acest capitol sunt prezentate
principalele caracteristici ale arborilor, algoritmi pentru calculul arborelui parţial de
cost minim, arbori direcţionaţi, arbori cu rădăcină şi arbori binari. Pe lângă operaţiile
primitive asupra arborilor – căutarea unei informaţii, inserarea unui nod, extragerea
unui nod şi metode de parcurgere, sunt prezentate două clase importante de arbori
binari, şi anume arbori de sortare şi arbori de structură.
Exemple:
4.1. Graful
1 3
4 2
este arbore, deoarece pentru orice pereche de vârfuri i,j, 1 ≤ i,j ≤ 6, i≠j, există un i-j
drum şi graful nu conţine cicluri.
48
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
4.2. Graful
1 3
4 2
5
6
nu este arbore deoarece este conex, dar drumul Γ :1,4,3,2,1 este un ciclu.
4.3. Graful
1 3 7
4 2
6 8
49
Structuri arborescente
3. G este graf aciclic maximal (prin adăugarea unei noi muchii în graf
rezultă cel puţin un ciclu).
Definiţia 4.1.3. Un graf orientat D=(V,E), cu proprietatea că pentru
∀u, v ∈ E, u, v ∈ E , atunci vu ∉ E se numeşte graf asimetric. Digraful D este
simetric dacă u , v ∈ E, uv∈E, dacă şi numai dacă vu∈E.
Exemple:
4.4. Arborele direcţionat
u
w x
t r y
v z
50
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
u v
z w
nu are rădăcină.
4.6. Arborele
y w
51
Structuri arborescente
Exemplu:
4.7. Arborele orientat
1
2 3 4
5 6 7 8 9
10 11 12 13 14 15
Dacă un vârf are p<n descendenţi, atunci primele p legături sunt către
descendenţi, iar ultimile n-p legături sunt nil. Pentru n ≤ 10, descrierea structurii de
date în limbajul Pascal este:
type ptnod=^nod;
nod=record
inf:integer;
leg:array[1..10] of ptnod;
end;
52
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
A. Parcurgerea în A-preordine
Iniţial vârful curent este rădăcina arborelui. Se vizitează vârful curent şi sunt
identificaţi descendenţii lui. Se aplică aceeaşi regulă de vizitare pentru arborii care au
ca rădăcini descendenţii vârfului curent, arborii fiind vizitaţi în ordinea dată de
numerele ataşate vârfurilor rădăcină corespunzătoare.
Exemplu:
4.8. Pentru arborele orientat din exemplul 4.7, prin aplicarea parcurgerii în
A-preordine, rezultă: 1,2,5,6,10,11,12,3,7,8,4,9,13,14,15.
Pentru un arbore reprezentat FIU-FRATE, implementarea parcurgerii în
A-preordine se bazează pe următoarea procedură recursivă, având ca parametru de
intrare rădăcina arborelui curent (vârful curent în momentul apelului).
B. Parcurgerea A-postordine
Regula de vizitare a vârfurilor în parcurgerea în A-postordine diferă de cea în
A-preordine numai prin faptul că rădăcina fiecărui arbore este vizitată după ce au fost
vizitate toate celelalte vârfuri ale arborelui.
Exemplu:
4.9. Pentru arborele orientat din exemplul 4.7, ordinea de vizitare a vârfurilor
este: 5,10,11,12,6,2,7,8,3,13,14,15,9,4,1.
Pentru arbori reprezentaţi prin structuri de date arborescente, implementarea
parcurgerii în A-postordine poate fi obţinută pe baza următoarei proceduri recursive.
Unicul parametru (de intrare) reprezintă rădăcina arborelui curent în momentul
apelului.
procedure A_postordine (R);
if R≠nil then
do-for i=1,n,1
A_postordine(R^.leg[i]);
53
Structuri arborescente
enddo;
vizit (R);
endif;
end;
C. Parcurgerea pe niveluri
Exemplu:
4.10. Pentru arborele din exemplul 4.7, prin aplicarea parcurgerii pe niveluri,
rezultă: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15.
procedure parcurgere_pe_niveluri(R,FIU,FRATE,n)
C:ptcoada;
C=nil;push(C,R);
while C≠nil do
pop(C,v);
VIZIT(v);
v=FIU[v];
while v≠0 do
push(C,v);
v=FRATE[v];
endwhile;
endwhile;
end;
54
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu
4.11. Pentru arborele de la exemplul 4.7, evoluţia algoritmului este:
C
t
t=1 1
t=2 2 3 4
t=3 3 4 5 6
t=4 4 5 6 7 8
t=5 5 6 7 8 9
t=6 6 7 8 9
t=7 7 8 9 10 11 12
t=8 8 9 10 11 12
t=9 9 10 11 12
t=10 10 11 12 13 14 15
t=11 11 12 13 14 15
t=12 12 13 14 15
t=13 13 14 15
t=14 14 15
t=15 15
t=16
procedure fraţi(v);
if v≠0 then
push(C,v);fraţi(FRATE[v];
endif;
end;
procedure parc;
if C≠nil then
pop(C,v);VIZIT(v);
fraţi(FIU[v]); parc;
endif;
end;
55
Structuri arborescente
Definiţia 4.1.9. Fie G graf. Subgraful parţial H este un arbore parţial al lui G
dacă H este graf arbore.
Definiţia 4.1.10. Fie (V,E,w) un graf ponderat conex. Dacă T=(V,E0) este un
arbore parţial al grafului G=(V,E), ponderea arborelui T este definită prin:
W(T)= ∑ w ( e) .
e∈E 0
56
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
57
Structuri arborescente
procedure preordine(r:arb);
begin
if r<>nil then
begin
write(r^.inf);
preordine(r^.fius);
preordine(r^.fiud);
end;
end;
58
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
4.12. Arborele binar
50
30 70
20 40 60 90
80
este de sortare.
Exemplu:
4.13. Pentru arborele de sortare descris în exemplul 4.12, parcurgerea în
inordine determină secvenţa de valori: 20,30,40,50,60,70,80,90, adică exact vectorul
de informaţii asociate nodurilor ordonat crescător.
59
Structuri arborescente
Exemplu:
4.14. Aplicarea algoritmul descris pentru inserarea informaţiei 55 în arborele
de sortare din exemplul 4.12 determină următoarele operaţii:
INF(v)=50<55: se decide inserarea în subarborele drept cu rădăcina având
informaţia ataşată 70 (cazul 2.c);
INF(v)=70>55: se decide inserarea în subarborele stâng cu rădăcina având
informaţia ataşată 60 (cazul 2.a);
INF(v)=60>55: se decide inserarea în subarborele stâng cu rădăcina nil
(situaţia de la 1). Se decide crearea nodului cu informaţie 55, fiu stâng al nodului de
informaţie 60.
Arborele rezultat este:
50
30 70
20 40 60 90
55 80
60
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemple:
4.15. Ştergerea informaţiei 70 în arborele de sortare din exemplul 4.12 este
realizată astfel:
70>50, decide ştergerea din subarborele drept (cu rădăcină 70) - situaţia 2.b;
70=70, decide ştergerea din arborele curent: rădăcina etichetată cu 70 -
situaţia 2.c;
61
Structuri arborescente
Există subarbore stâng (rădăcina lui p este etichetată cu 60) iar acesta nu are
subarbore drept - situaţia 2.c.1.: nodul cu informaţie 70 este etichetat cu 60, iar p este
înlocuit cu subarborele său stâng (vid)
Arborele de sortare rezultat este:
50
30 60
20 40 90
80
50
30 70
20 40 60 90
10 25
24
62
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Există subarbore stâng (rădăcina este etichetată cu 20), iar acesta are
subarbore drept - situaţia 2.c.2: nodul cu informaţie 30 este etichetat cu 25
(informaţia ultimului nod p detectat la c2)), iar p este înlocuit cu subarborele său
stâng (cu rădăcină 24).
Arborele rezultat este:
50
25 70
20 40 60 90
10 24
63
Structuri arborescente
Exemplu:
4.17. Pentru expresia matematică (a+b)*(c-d)+e/g, arborele de structură este:
∗ /
+
− e g
a b c d
64
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
4.18. Etapele calculului sistemului de priorităţi pentru expresia de la
exemplul 4.17 sunt:
65
Structuri arborescente
Exemplu:
4.19. Pentru expresia de la exemplul 4.17, după determinarea vectorului de
priorităţi şi a expresiei neparantezate corespunzătoare, procedura cr_der realizează
următoarea construcţie:
s=a+b*c-d+e/g,
prioritate=(maxint,11,maxint,10,maxint,11,maxint,1,maxint,10,maxint)
în construcţie în construcţie
* în construcţie
în construcţie în construcţie
66
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
∗ în construcţie
+ în construcţie
în construcţie în construcţie
∗ în construcţie
+ în construcţie
a în construcţie
67
Structuri arborescente
∗ în construcţie
+ în construcţie
a b
∗ în construcţie
+ −
a b în construcţie în construcţie
68
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
∗ în construcţie
+ −
a b c în construcţie
∗ în construcţie
+ −
a b c d
69
Structuri arborescente
∗ /
+ în construcţie în construcţie
−
a b c d
∗ /
+ în construcţie
− e
a b c d
70
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
∗ /
+
− e g
a b c d
şi construcţia se termină.
Exemplu:
4.20. Pentru expresia considerată la exemplul 4.17, forma poloneză directă
este +*+ab-cd/eg. Forma poloneză inversă a expresiei date este ab+cd-*eg/+.
71
Structuri arborescente
Exemplu:
4.21. Prin aplicarea traversării SRD a arborelui de structură al expresiei din
4.2.6, rezultă:
s=(((a)+(b))*((c)-(d)))+((e)/(g)).
Se observă că, prin aplicarea traversării SRD şi a parantezării descrise,
expresia rezultată are aceeaşi semnificaţie cu expresia iniţială, dar apar multe
paranteze “inutile”. Propunem ca exerciţiu scrierea unei proceduri Pascal pentru
eliminarea parantezelor inutile.
Exemplu:
4.22. Prin aplicarea metodei de evaluare descrise, se obţine:
18
15 3
5 3 6 2
3 2 5 2
72
ALGORITMI RECURSIVI
METODELE DIVIDE ET IMPERA
ŞI BACKTRACKING
function Fact(n:byte):word;
begin
if n=0 then Fact:=1
else Fact:=n*Fact(n-1);
end;
n!
Utilizarea formulei C kn = pentru calculul combinărilor (n, k date)
k!(n − k )!
ridică dificultăţi deoarece n!, pentru n ≥ 13, nu poate fi reprezentat în calculator ca
dată de un tip întreg, chiar dacă numărul C nk este relativ mic şi poate fi reprezentat
ca întreg. Pe baza relaţiei de recurenţă C nk = C nk−1 + C nk−−11 rezultă ceea ce este
cunoscut sub numele de triunghiul lui Pascal.
De exemplu, triunghiul lui Pascal, pentru n = 7, este:
73
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
var
n,i,j:word;
x:array[0..13,0..13] of word;
begin
write(‘Valoarea pentru n=’);
readln(n);
if n>13 then
writeln(‘Eroare’)
else
begin
x[0,0]:=1;
for i:=1 to n do
begin
x[i,0]:=1;
x[i,i]:=1;
end;
for i:=1 to n-1 do
for j:=1 to i do
x[i+1,j]= x[i,j-1]+x[i,j];
end;
for i:=0 to n do
begin
for j:=0 to i do write(x[i,j],’ ‘);
writeln;
end;
end.
74
Algoritmi recursivi. Metodele divide et impera şi backtracking
function comb(n,k:byte):word;
begin
if k>n then comb:=0
else
if (k=0) or (k=n) then comb:=1
else comb:=comb(n-1,k)+comb(n-1,k-1);
end;
Fiecare apel Fib(n) pentru n>1 determină încă două apeluri: Fib(n-1) şi
Fib(n-2). Numărul total de apeluri efectuate până la rezolvarea problemei Fib(n)
creşte exponenţial, în funcţie de parametrul n. Mai mult, rezolvarea problemei
Fib(n-2) are loc atât la apelul Fib(n-1), cât şi la apelul determinat de Fib(n). Datorită
acestor inconveniente, este preferată o soluţie iterativă pentru calculul unui termen de
rang dat al şirului lui Fibonacci.
75
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
2 x + 1, x < 3 x 2 − 3x + 2, x ≤ 1
f (x) = 2 , g( x ) =
x + 2, x ≥ 3 3x + 5, x > 1
76
Algoritmi recursivi. Metodele divide et impera şi backtracking
3 2
Fact=3*Fact(2) Fact=2*Fact(1)
Fact=3*Fact(2)
(o)
Adresa de revenire (o)
1 0
Fact=1*Fact(0) Fact=1
Adresa de revenire Adresa de revenire
2 1
Fact=2*Fact(1) Fact=1*Fact(0)
Fact=3*Fact(2) Fact=2*Fact(1)
(o) Fact=3*Fact(2)
77
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
1 2
Fact=1 Fact=2
2 3
Fact=2*Fact(1) Fact=3*Fact(2)
Fact=3*Fact(2)
Fact=6
(○)
Adresa de revenire
Adresa de revenire (○)
Stiva vidă
function f(x:real):real;
begin
if x<3 then f:=2*x+1
else f:=x*x+2
end;
function g(x:real):real;
begin
if x<=1 then g:=x*x-3*x+2
else g:=3*x+5
end;
function h(x:real):real;
begin
h:=f(g(f(x)));
end;
78
Algoritmi recursivi. Metodele divide et impera şi backtracking
79
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
intervalului (a,b), atunci este îndeplinită una şi numai una dintre relaţiile f(a)f(c)<0,
f(c)=0, f(b)f(c)<0. Dacă f(c)=0, atunci x0=c. Dacă f(a)f(c)<0 atunci x 0 ∈ (a , c) ,
altfel x 0 ∈ (c, b) . Se presupune că f(a)f(c)<0. Se poate aplica acelaşi procedeu
intervalului (a,c) şi se continuă până când sau este obţinută soluţia exactă sau
intervalul care conţine soluţia este de lungime inferioară lui ε . În cazul în care
terminarea calculului se realizează prin identificarea unui interval de lungime
inferioară lui ε , x̂ poate fi considerat oricare dintre numerele din acel interval, de
exemplu mijlocul intervalului. Evident, numărul maxim de iteraţii N pentru obţinerea
b−a b − a
preciziei ε rezultă din inegalitatea < ε , adică N = log 2 + 1 .
ε
N
2
Se presupune că funcţia f este calculată prin apelul funcţiei Pascal
f(a:real):real. O variantă recursivă a metodei descrise este:
uses crt;
{$F+}
type
fct=function(x:real):real;
var
eps,a,b,x:real;
functie:fct;
function f(x:real):real;
begin
f:=x*x*x-8;
end;
80
Algoritmi recursivi. Metodele divide et impera şi backtracking
Unul dintre cei mai eficienţi algoritmi pentru sortarea crescătoare a unei
secvenţe de numere reale este cunoscut sub numele de algoritmul de quicksort
(sortare rapidă) şi reprezintă, de asemenea, un exemplu de aplicare a metodei divide
et impera. Fie secvenţa (vp , vp+1 , …,vu ), unde iniţial p=1, u=n (n=dimensiunea
vectorului).
Dacă p = u, secvenţa este sortată.
Altfel, se poziţionează vp în această secvenţă astfel încât toate elementele ce
ajung în faţa lui să fie mai mici decât el şi toate cele care îi urmează să fie mai mari
decât el; fie poz poziţia lui corectă în secvenţa (vp , vp+1 , …, vpoz , vpoz+1 , …,vn).
Procedeul se reia pentru secvenţele (vp , vp+1 , …,v poz-1) şi (vpoz+1, vpoz+2, …,vn), deci
p ≤ poz-1 şi poz+1 ≤ u.
Poziţionarea elementului vp se face astfel: se utilizează doi indicatori, i şi j;
iniţial i = p şi j = u. Se compară vi cu vj, dacă nu este necesară interschimbarea, se
micşorează j cu 1, repetându-se procesul; dacă apare o interschimbare, se măreşte i
cu 1 şi se continuă compararea, mărind i până la apariţia unei noi interschimbări.
Apoi se micşorează din nou j, continuându-se în acelaşi mod până când i = j.
Exemplu:
5.1. Aplicarea algoritmului de sortare rapidă pentru
v = (10, 12, 19, 15, 3, 17, 4, 18)
1 2 3 4 5 6 7 8
p = 1, u = n = 8
a) poziţionarea lui vp = v1 = 10
i=1, j=8
Sensul de parcurgere: ←
i=1, j=8 10<18 ⇒ j=j-1
i=1,j=7 10>4 ⇒ se efectuează interschimbarea şi se atribuie i=i+1=2
v=(4, 12, 19, 15, 3, 17, 10, 18)
i=2, j=7
Sensul de parcurgere: →
i=2, j=7 12>10 ⇒ se efectuează interschimbarea şi se atribuie j=j-1=6
v=(4, 10, 19, 15, 3, 17, 12, 18)
i=2, j=6
Sensul de parcurgere: ←
i=2, j=6 10<17 ⇒ j=j-1
i=2, j=5 10>3 ⇒ se efectuează interschimbarea şi se atribuie i=i+1=3
v = (4, 3, 19, 15, 10, 17, 12, 18)
i=3, j=5
Sensul de parcurgere: →
i=3, j=5 19>10 ⇒ se efectuează interschimbarea şi se atribuie j=j-1=4
81
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
82
Algoritmi recursivi. Metodele divide et impera şi backtracking
sv3=(12) sortată
sv4=(17, 19, 18)
6 7 8
În acest moment, v=(3, 4, 10, 12, 15, 17, 19, 18)
83
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
begin
i:=p;j:=u;di:=0;dj:=-1;
while i<j do
if x[i]>x[j] then
begin
v:=x[i];
x[i]:=x[j];
x[j]:=v;
l:=di;di:=-dj;dj:=-l;
i:=i+di;j:=j+dj;
end
else begin
i:=i+di;j:=j+dj;
end;
k:=i;
end;
procedure quick(p,u:byte);
var
i:byte;
begin
if p>=u then
else begin
poz(p,u,i);
quick(p,i-1);
quick(i+1,u);
end;
end;
begin{ program principal}
clrscr;
write('Dimensiunea vectorului:');
readln(n);
for i:=1 to n do
read(x[i]);
quick(1,n);
for i:=1 to n do
write(x[i],' ');
end.
84
Algoritmi recursivi. Metodele divide et impera şi backtracking
s-a redus, respectiv în care s-a descompus. De asemenea, pentru fiecare dintre
problemele considerate au fost definite subproblemele primitive (condiţiilor
terminale) a căror soluţie este “cunoscută” sau dată. Metoda de rezolvare se numeşte
divide et impera (dezbină şi stăpâneşte) şi semnifică ideea prin care este realizată
construcţia soluţiei.
85
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
care s-a ajuns pe baza opţiunii x1 ş.a.m.d. După efectuarea acţiunii corespunzătoare
alegerii alternativei xn rezultă o stare finală.
Forma standard a metodei corespunde unei probleme în care trebuie găsit un
drum soluţie x=(x1, x2, ..., xn) cu xi ∈ Si, unde fiecare mulţime Si este finită şi conţine
si elemente. În plus, se presupune că fiecare Si este ordonată şi reprezintă mulţimea
alternativelor existente la momentul i al căutării.
În anumite cazuri interesează obţinerea unei singure soluţii, în altele sunt
căutate toate soluţiile problemei sau cele care îndeplinesc un criteriu dat (de
exemplu, se maximizează sau minimizează o funcţie f definită pe mulţimea
drumurilor soluţie din spaţiul stărilor).
Procesul de căutare a unui drum soluţie revine la tentativa de extindere a
porţiunii de drum construit, alegând prima alternativă disponibilă pentru starea
curentă atinsă. Continuarea drumului poate fi realizată până la atingerea unei stări
finale sau până la întâlnirea unei stări capcană (mulţimea vidă de alternative). Dacă
este atinsă o stare capcană, atunci este necesară revenirea la starea anterioară şi
selectarea următoarei alternative disponibile acestei stări. Dacă nu mai există
alternative disponibile, atunci se iniţiază o nouă revenire ş.a.m.d. În cazul în care
există cel puţin încă o alternativă disponibilă, atunci se reia procesul de extindere a
drumului rezultat. În condiţiile în care revenirea poate conduce la atingerea stării
iniţiale şi pentru ea nu mai există alternative disponibile, se consideră că problema nu
are soluţie.
Pentru implementarea căutării este necesară reţinerea alternativei selectate
pentru fiecare stare atinsă până la cea curentă, astfel încât, în cazul unei reveniri să
fie posibilă alegerea alternativei următoare. Cu alte cuvinte, procesul de căutare
revine la tentativa de extindere a drumului curent (pasul de continuare), cu eventuala
revenire în cazul atingerii unei stări capcană (pasul de revenire - back), memorând
alternativele selectate pentru fiecare stare intermediară atinsă (track). De aici îşi are
geneza numele metodei backtracking.
86
Algoritmi recursivi. Metodele divide et impera şi backtracking
procedure back(k:byte);
begin
if k=n+1 then final
else
begin
x[k]:=init(k);
while succ(k) do
if continuare(k) then back(k+1);
end;
end;
în care:
− final este o procedură care descrie prelucrarea dorită pentru o soluţie
determinată (se afişează rezultatul, se testează o funcţie criteriu pentru soluţia
obţinută samd);
− init(k) efectuează iniţializarea lui xk cu o valoare prin care se indică faptul
că, până la acel moment, nu a fost selectată nici o alternativă pentru poziţia k;
− succ(k) este o funcţie booleană care calculează true, dacă şi numai dacă
există succesor pentru xk în Sk;
− continuare(k) este o funcţie booleană pentru testarea condiţiilor de
continuare; calculează true dacă şi numai dacă este posibilă extinderea drumului
curent.
87
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
program permutare;
uses crt;
type tip_elem=0..7;
var x:array[1..7] of tip_elem;
n:byte;
function init:byte;
begin
init:=0;
end;
function succ(k:byte):boolean;
begin
succ:=x[k]<n; {atribuire de valoare logica}
inc(x[k]);
end;
function continuare(k:byte):boolean;
var i:byte;
begin
i:=1;
while(i<k)and(x[i]<>x[k]) do inc(i);
continuare:=i=k; {atribuire de valoare logica}
end;
procedure final;
var i:byte;
begin
for i:=1 to n do write(x[i],' ');
readln;
end;
procedure back(k:byte);
begin
if k=n+1 then final
else
begin
x[k]:=init;
while succ(k) do
if continuare(k) then back(k+1);
88
Algoritmi recursivi. Metodele divide et impera şi backtracking
end;
end;
begin
clrscr;
write('Numarul de elemente ale permutarii: ');
readln(n);
back(1);
end.
În cazul acestei probleme este posibilă operarea unor simplificări în scrierea
codului, pe baza observaţiilor:
- funcţia init(k) nu depinde de valoarea lui k şi returnează întotdeauna
valoarea 0;
- funcţia succ(k) nu depinde de valoarea parametrului k şi realizează
întotdeauna o incrementare (S1 = S2 = ... =Sn = {1, 2,..., n}).
Procedura back(k) poate fi descrisă fără a utiliza funcţiile init şi succ, astfel:
procedure back(k:byte);
var i:byte;
begin
if k=n+1 then final
else
for i:=1 to n do
begin
x[k]:=i;
if continuare(k) then back(k+1);
end;
end;
Înlocuirea în procedura back a structurii repetitive while cu ciclul for este
posibilă datorită faptului că funcţia succ realiza incrementarea valorii elementului
x[k].
program permutare_1;
uses crt;
type tip_elem=0..7;
var x:array[1..7] of tip_elem;
n:byte;
procedure final;
var i:byte;
begin
for i:=1 to n do write(x[i],' ');
readln;
end;
function continuare(k:byte):boolean;
var i:byte;
begin
i:=1;
while(i<k)and(x[i]<>x[k]) do inc(i);
continuare:=i=k;
end;
procedure back(k:byte);
89
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
var i:byte;
begin
if k=n+1 then final
else
for i:=1 to n do
begin
x[k]:=i;
if continuare(k) then back(k+1);
end;
end;
begin
clrscr;
write('Numarul de elemente ale permutarii: ');
readln(n);
back(1);
end.
0 ≤ x[i] ≤ y[2, i], i = 1,..., n . Sunt generate toate descompunerile posibile ale sumei
s funcţie de bancnotele disponibile şi, la fiecare moment în care este determinată o
astfel de descompunere, aceasta este comparată cu precedenta. În vectorul xb este
memorată o cea mai bună descompunere pe baza criteriului f, după fiecare astfel de
comparaţie.
program bancnote;
uses crt;
var x,xb:array[1..100] of longint;
y:array[1..2,1..20]of longint;
n,i,nrb:word;
s:longint;
function init(k:word):longint;
begin
init:=-1;
end;
function urm(k:word):boolean;
begin
90
Algoritmi recursivi. Metodele divide et impera şi backtracking
urm:=x[k]<y[2,k];inc(x[k]);
end;
function continuare(k:word):boolean;
var
sc,i:longint;
begin
sc:=0;
for i:=1 to k do sc:=sc+x[i]*y[1,i];
if k<n then continuare:=sc<=s
else continuare:=sc=s;
end;
procedure final(k:word);
var i:byte;
nr:longint;
begin
nr:=0;
for i:=1 to n do
begin
nr:=nr+x[i];
end;
if nr<nrb then
begin
for i:=1 to n do xb[i]:=x[i];
nrb:=nr;
end;
end;
procedure back(k:byte);
begin
if k=n+1 then final(k)
else
begin
x[k]:=init(k);
while urm(k) do
if continuare(k) then back(k+1)
end;
end;
begin
clrscr;
write('Dati numarul de bancnote');
readln(n);
writeln('Valorile si numarul de exemplare:');
for i:=1 to n do
begin
write('Valoarea:');readln(y[1,i]);
write('Numar de bancnote:');readln(y[2,i]);
end;
write('Suma schimbata:');
readln(s);
nrb:=maxint;
back(1);
writeln('Numarul minim de bancnote:',nrb);
writeln;
writeln('Numarul de bancnote alese din fiecare tip:');
for i:=1 to n do
writeln('Tip ',i,' numar de bancnote alese:',xb[i]);
end.
91
REPREZENTAREA VIZUALĂ
A DATELOR
Placa grafică
Interfaţa video
Monitor
(Logică de comandă)
Software
Memoria ecran
• Memoria ecran, de tipul RAM, are rolul de a stoca, sub formă binară,
imaginea care se afişează pe ecran. Ea poate avea diferite capacităţi - uzual, până la 1
92
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Magistrala de date
Microproceso Memoria
r Magistrala de adrese ecran
Interfaţă video
Sincronizare
Controler Generator Registru de Generator
video de caractere deplasare de semnale
Semnale de comandă
Monitor
Fig. 6.2 Modul de interacţiune a interfeţei video
93
Reprezentarea vizuală a datelor
•••• ••••
•••• ••••
y (xmax,ymax)
Fig. 6.3 Spaţiul ecran
94
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
(AH), iar alţi parametri, dacă sunt necesari, se încarcă în alte registre (AL, BH, BL,
DH, DL).
012 cmax x
0
1 (nr. coloană)
caracter 8
#•#####•
#•#####•
#••####•
#••####•
lmax #•#•###•
#•#••##•
#•##•##•
14 #•##••#•
lmax =24 #•###•#•
y (nr. linie) ecran 25*80 #•####••
#•####••
cmax =79 #•#####•
#•#####•
########
lmax =24
ecran 25*40
cmax =39
a) Spaţiul b) Matricea de caractere
95
Reprezentarea vizuală a datelor
cod 0 1 ... 53
Culoare de Culoare de
fond caracter
Intensitatea
Clipire
culorii
(1=da, 0=nu)
(1=da, 0=nu)
96
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
97
Reprezentarea vizuală a datelor
O pagină video utilizează un bit pentru a defini starea fiecărui pixel al ecranului
(1=aprins, 0=stins) şi un număr de biţi suplimentari pentru a defini culoarea
acestora. Deoarece culorile se definesc pe baza registului de paletă, dacă se
utilizează concomitent 16 culori, sunt necesari 4 biţi suplimentari. Rezultă că o
pagină video trebuie să aibă cel puţin 4∗rezoluţie biţi. De exemplu, pentru o placă
EGA, cu rezoluţie înaltă, sunt necesari 4∗640∗350=896000 biţi≈110 Kb şi deci,
într-o memorie ecran de 256 Kb, pot fi create două pagini video.
Modul grafic utilizează posibilitatea de adresare a pixelilor într-o pagină
video şi un cursor grafic invizibil. Cursorul grafic are coordonatele ultimului pixel
adresat în pagina video selectată şi poate fi deplasat în orice punct al acestui spaţiu.
Se menţin, de asemenea, facilităţile de interogare asupra poziţiei cursorului şi asupra
culorii pixelului curent.
Ca şi la modul text, la definirea unui pixel există posibilitatea realizării
operaţiei SAU EXCLUSIV între biţii de culoare ai unui pixel dintr-o pagină şi
valorile noi ale acestora. În acest fel, pixelii pot fi scrişi sau şterşi.
Toate aceste operaţii se realizează ca servicii ale rutinei INT 10h. În modul
grafic, se selectează un anumit submod acceptat de placa grafică, submodurile
diferind prin rezoluţie, număr de culori şi număr de pagini de ecran pe care le
acceptă.
TextMode (Mode);
în care parametrul Mode, de tip Word, defineşte modul dorit, prin numărul asociat.
Procedura salvează vechiul mod în variabila predefinită LastMode, de unde, prin
aceeaşi procedură, poate fi restaurat. Procedura setează culoarea de fond zero
(culoarea neagră). Dacă nu se apelează TextMode, atunci, implicit, se consideră
modul de mare rezoluţie al plăcii grafice.
98
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
6.1.
. . .
User CRT;
. . .
TextMode(C080);
. . .
TextMode(LastMode);
. . .
TextMode(BW40)
Exemplu:
6.2.
. . .
TextColor (Red+128);
. . .
TextColor(Green+Blink);
. . .
Culoare:=6
. . .
TextColor(Culoare);
99
Reprezentarea vizuală a datelor
100
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
101
Reprezentarea vizuală a datelor
102
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
103
Reprezentarea vizuală a datelor
Exemple:
6.5.
GraphDriver:=EGA;
GraphMode:=EGALo;
InitGraph ('GraphDriver, GraphMode,');
6.6.
GraphDriver:=Detect;
GraphMode:=0;
InitGraph (GraphDriver, GraphMode,'C:\TP\BIN');
c) Să verifice dacă iniţializarea modului grafic dorit, din pasul anterior, s-a desfăşurat
cu succes. În acest sens, se utilizează funcţia specială GraphResult, de tipul
INTEGER şi fără parametri, care returnează valoarea zero (GrOK) pentru succes.
Funcţia poate fi utilizată după execuţia oricărei operaţii grafice.
d) Să apeleze subrutinele grafice necesare, dacă iniţializarea s-a derulat cu succes.
e) Să revină în modul text, la terminarea operaţiilor grafice, prin utilizarea procedurii
fără parametri, CloseGraph. Iniţializarea sistemului grafic alocă memoria dinamică
necesară, iar închiderea o eliberează.
104
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
x e − x 0e v − v1
= 2
x r − x r w 2 − w1
0
(1)
y e − y 0e v 4 − v3
=
y 0r − y r w 3 − w 4
x
(v1,v3)
(x 0e , y e0 )
•
y •
(xe,ye)
y
(v2,v4)
vizor
(w1,w3)
•
(x 0r , y 0r ) • x
(xr,yr)
fereastră (w2,w4)
Fig. 6.6 Relaţia fereastră-vizor
105
Reprezentarea vizuală a datelor
v 2 − v1 v − v1 0
x e = x 0e + xr − 2 xr
w 2 − w1 w 2 − w 4
(2)
v − v3 v − v3 0
y e = y 0e − 4 yr + 4 yr
w3 − w4 w 3 − w 4
din care, prin înlocuirea coordonatelor centrelor ferestrelor şi vizorului, în funcţie de
coordonatele colţurilor acestora, se obţin relaţiile:
v1 + v 2 v − v1 v − v1 w 1 + w 2
xe = + 2 xr − 2 ⋅
2 w 2 − w1 w 2 − w1 2
(3)
v3 + v4 v4 − v3 v4 − v3 w 3 + w 4
ye = − yr + ⋅
2 w3 − w4 w3 − w4 2
Relaţiile (6) dau coordonatele absolute, în spaţiul ecran, pentru orice punct
(xr,yr). Este, de multe ori, preferabil să se exprime punctele în spaţiul ecran relativ la
vizor, adică într-un subspaţiu cu originea în colţul stânga-sus al acestuia. Dacă se
notează (dxe,dye) coordonatele absolute (xe, ye), se obţin relaţiile:
x e = v 1 + dx e
(7)
y e = v 3 + dy e
106
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
v 2 − v1
dx e = (x r − w 1 )
w 2 − w1
(8)
v4 − v3
dy e = − ( y r + w 3 )
w3 − w4
Dacă se notează:
v 2 − v1
k 1 =
w 2 − w 1
v 4 − v3
k 2 = −
w 3 − w 4
dx e = round(k 1 ( x r − w 1 ))
(9)
dy e = round(k 2 ( y r − w 3 ))
Utilizarea ferestrei şi/sau vizorului în tehnica realizării imaginilor grafice
oferă, în plus, posibilitatea de verificare a apartenenţei punctelor la desenul curent.
Se pot implementa măsuri de eliminare a punctelor străine (tehnica clipping-ului sau
tăierii), pentru a asigura protecţia imaginilor vecine împotriva distrugerilor
accidentale, desenându-se numai imaginea din fereastră.
În sistemul grafic Pascal, ideile tehnicii fereastră-vizor sunt implementate
parţial, aşa după cum rezultă din cele ce urmează.
• Există posibilitatea definirii vizorului ca spaţiu curent pentru un anumit
desen (vizor curent). Declararea lui se face prin apelul procedurii SetViewport, în
forma:
SetViewport(v1,v3,v2,v4,Clip);
în care (v1,v3) şi (v2,v4) sunt constante sau variabile de tipul INTEGER care dau
coordonatele colţurilor vizorului, iar Clip este un parametru boolean care activează
sau inhibă aplicarea clipping-ului.
Unitatea Graph defineşte funcţiile de tipul INTEGER fără parametri:
GetMaxX şi GetMaxY care returnează dimensiunile maxime ale spaţiului ecran
pentru placa grafică cu care este echipat calculatorul. Pentru a crea independenţă
programului faţă de calculator, se recomandă utilizarea lor în definirea vizorului. De
asemenea, unitatea Graph defineşte constantele simbolice ClipOn (True) şi ClipOff
(False) care pot fi folosite pentru parametrul Clip.
107
Reprezentarea vizuală a datelor
Exemplu:
6.7.
SetViewport(10,25,GetMaxX-150,GetMaxY-50,ClipOn);
Vizorul implicit, stabilit la iniţializarea modului grafic, este întregul ecran, echivalent
cu un apel de forma:
SetViewport (0,0,GetMaxX,GetMaxY,ClipOn) ;
Exemplu:
6.8. Pentru un ecran monocrom (dar nu numai) se poate utiliza o secvenţă de
forma:
Rectangle(9,24,GetMaxX-149,GetMaxY-49);
SetViewport(10,25,GetMaxX-150,GetMaxY-50,ClipOn)
ClearViewport;
în care procedura Rectangle desenează un dreptunghi cu liniile în culoarea curentă de
desen (alb), iar SetViewport îl şterge, la culoarea fondului.
dx e = x e − v1
dy e = y e − v 3
108
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
109
Reprezentarea vizuală a datelor
Exemplu:
6.9. Presupunând o placă EGA, secvenţa care urmează stabileşte un vizor cu
fond albastru în care desenează un dreptunghi umplut. După 2000 ms se refac
culorile anterioare.
Uses Graph,Crt;
VAR
ColorBk,ColorFg:Word;
……………………..
ColorBk:=GetBkColor;
ColorFg:=GetColor;
SetViewport(20,50,250,150,ClipOn);
SetColor(EGARed);
SetBkColor(EGABlue);
ClearViewport;
Bar(20,10,60,50);
Delay(2000);
SetBkColor(ColorBk);
SetColor(ColorFg);
. . . . . . . . . . . . . .
110
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Stilul definit rămâne valabil până la o nouă setare. Stilul implicit este linia
continuă normală. Parametrii, de tipul WORD, constante sau variabile, definesc
atributele liniei (stilul liniei):
- TipStil - defineşte tipul de linie din punctul de vedere al continuităţii.
Parametrul poate avea valorile 0-4 sau constanta simbolică
corespunzătoare;
- Grosime - precizează grosimea, în număr de pixeli şi poate avea valorile:
1 (linie normală - NormWidth) şi 2 (linie groasă - ThickWidth);
- Model - defineşte un stil de utilizator printr-un fragment de 16 pixeli. Un pixel
este aprins, dacă bitul corespunzător, din numărul dat de acest
parametru, este unu. De exemplu, dacă Model=$F053 (în binar 1111
0000 0101 0011) atunci se defineşte modelul •••• xxxx x•x• xx••, în care •
este pixel aprins.
Desenarea unui segment de dreaptă se face prin procedura
Line(x1,y1,x2,y2). Prin parametrii x1, y1, x2, y2, de tip INTEGER, se precizează
coordonatele capetelor segmentului de dreaptă. Coordonatele sunt relative la vizorul
curent şi se pot exprima ca variabile sau constante. Se desemnează numai porţiunea
de segment care se află în interiorul vizorului.
Exemplu:
6.10. Secvenţa care urmează:
SetViewport(20,20,150,100,ClipOn);
Line(10,10,150,150);
111
Reprezentarea vizuală a datelor
(0,0) x
(20,20)
•
(30,30)
•
(138,100
• • (150,100)
• (170,120)
y
Fig. 6.7. Desenarea liniilor relativ la vizor
Punctul curent poate fi mutat într-un alt loc din spaţiul fizic, prin procedurile
MoveTo şi MoveRel care se apelează sub forma: MoveTo(x,y); MoveRel(x,y).
Procedura MoveTo consideră valorile (x,y) drept coordonate relative la vizorul curent
şi calculează coordonatele absolute (xe,ye) ale punctului curent prin relaţiile: xe=v1+x;
ye=v3+y, unde (v1,v3) sunt coordonatele originii vizorului. Procedura MoveRel
deplasează punctul curent relativ la poziţia sa anterioară, adică xe=GetX+x;
ye=GetY+y, în care x,y apar ca deplasări. La intrarea în regim grafic, la definirea
şi/sau ştergerea vizoarelor, precum şi la schimbarea modului grafic, punctul grafic
curent este nedefinit. Este sarcina programului să iniţializeze punctul curent prin
procedura MoveTo. Toate procedurile de desenare de linii şi scriere de text
deplasează poziţia punctului curent în ultimul punct utilizat.
Desenarea liniilor care îşi au originea în punctul curent se realizează cu
ajutorul procedurilor LineTo(x,y); LineRel(x,y). Pentru LineTo, coordonatele
extremităţii liniei sunt relative la vizorul curent şi deci xe=v1+x, ye=v3+y, unde (xe,ye)
sunt coordonatele absolute ale extremităţii liniei relativ la punctul curent xe=GetX+x;
ye=GetY+y. După trasare, punctul curent este deplasat în punctul xc=xe, yc=ye. Dacă
la trasare a fost necesară operaţia de clipping, punctul final al segmentului vizibil este
recalculat ca (x ~ , y ~ ) , însă punctul curent este mutat tot în (xe,ye). Se evită astfel
e e
deformarea desenelor.
Exemplu:
6.11. Dacă se consideră linia frântă P1, P2, P3, P4 din figura 6.8, atunci
secvenţele de instrucţiuni de trasare, având în vedere coordonate relative la vizor şi
relative la punctul curent, pot fi scrise astfel:
112
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
(0,0) x
(20,10)
(70,40) (110,30)
[20,-20] [25,-15]
•
•
•
(50,60) •
(85,45)
[15,5]
y
Fig. 6.8. Desenarea segmentelor relativ la vizor şi la punctul curent
În plus, trebuie menţionat faptul că desenarea unei linii simple sau frânte
poate fi făcută astfel încât să distrugă sau să conserve, într-un anumit sens, culorile
pixelilor care se suprapun. Comportamentul pixelilor depinde de modul de scriere a
noii imagini în raport cu imaginea existentă pe ecran (setarea modului Write);
113
Reprezentarea vizuală a datelor
Exemplu:
6.12. Desenarea unui pătrat, cu latura orizontală CD de m=50 pixeli şi vârful
stânga sus în (30,60), pe un vizor egal cu întregul ecran. Deoarece k=1, se poate
utiliza secvenţa:
GetAspectRatio(Lpx,Ipy);
Ra:=Ipy/Lpx;
n=Round(50/Ra);
MoveTo(30,60);
LineTo(80,60);
LineTo(80,60+n);
LineTo(30,60+n);
LineTo(30,60);
114
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
d) Direcţia de scriere poate fi: orizontală sau verticală. Pentru direcţia orizontal
(constanta simbolică HorizDir) scrierea se face de la stânga spre dreapta, iar pentru
direcţia vertical (VertDir), de jos în sus;
e) Alinierea: precizează, pentru fiecare din cele două direcţii, modul de
amplasare a textului în raport cu punctul de scriere (punct de referinţă).
Pentru definirea atributelor se pot utiliza procedurile:
SetTextStyle (Font, Directie, Marime);
SetTextJustify (FtHorizDir, FtVertDir);
SetUserCharSize (MultX, DivX, MultY, DivY);
Parametrul de mărime, la procedura SetTextStyle, poate lua valori naturale
mai mari sau egale cu zero şi precizează de câte ori să fie mărite, proporţional,
caracterele fontului ales faţă de dimensiunea normală a acestora. Pentru definirea
dimensiunilor de utilizator (independente pe cele două direcţii), valoare zero a
parametrului Marime declară intenţia programatorului de a apela procedura
SetUserCharSize. La această procedură, se utilizează ideea că dimensiunea se
modifică prin înmulţire cu un raport. Raportul MultX/DivX va fi utilizat pentru a
modifica lăţimea caracterelor, iar MultY/DivY pentru modificarea înălţimii. Atunci
când raportul este unu, dimensiunea pe direcţia respectivă va rămâne nemodificată.
La procedura de definire a alinierii se cere să se precizeze modul de aliniere
a textului pe ambele direcţii, chiar dacă scrierea utilizează numai una din ele.
Exemple:
6.13. Se defineşte fontul SmallFont, cu mărire proporţională, de 3 ori şi cu
afişare centrată pe orizontală:
SetTextJustify(CenterText,CenterText);
SetTextStyle(SmallFont,HorizDir,3);
SetTextStyle(SansSerifFont,VerDir,0);
SetUserCharSize(1,1,3,2);
SetTextJustify(LeftText,BottomText);
115
Reprezentarea vizuală a datelor
Exemplu:
6.15. Se scrie orizontal textul "TURBO PASCAL", de 4 ori, cu cele patru
fonturi vectoriale. Fiecare rând, centrat pe linia verticală a centrului ecranului, se
scrie cu o altă culoare.
Clear Device;
SetTextJustify(CenterText,CenterText);
y:=20;
For i:=1 to 4 do
Begin
SetColor (i);
SetTextStyle(i,HorizDir,2+i);
Y:=y+TextHeight('TURBO PASCAL')+8;
OutTextXY(GetMaxX Div 2,Y,'TURBO PASCAL');
End;
116
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
6.16.
CONST
MyPattern:Array[1..8]of Byte=($01,$01,$FF,$01,$01,$FF,$01,$01);
. . . . . . . . . .
SetFillStyle(MyPattern,3);
117
Reprezentarea vizuală a datelor
DrawPoly(NumarVarfuri,CoordonateVarfuri);
FillPoly(NumarVarfuri,CoordonateVarfuri);
Procedurile sunt similare perechii de proceduri pentru desenarea
dreptunghiurilor. Coordonatele punctelor liniei poligonale trebuie să fie declarate ca
o variabilă de tip masiv, la care tipul de bază trebuie să fie PointType, adică un
articol cu două câmpuri: X şi Y. Dacă se doreşte trasarea unui poligon, atunci faţă de
linia poligonală cu n vârfuri trebuie declarate n+1 vârfuri şi coordonatele ultimului
vârf trebuie să coincidă cu cele ale primului. Procedura FillPoly se aplică numai
pentru poligoane, iar interiorul acestora este umplut cu haşura şi culoarea cerute.
Exemplu:
6.17.
CONST
Pentagon: Array[1…6]of PointType=
((x:20;y:45),(x:50;y:25),(x:100;y:10),
(x:120;y:40),(x:80;y:60),(x:20;y:45));
FillPoly (6,Pentagon);
118
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
6.18.
Arc (150,100,0,270,50);
PieSlice (GetMaxX Div 2,GetMaxY Div 2,0,360,GetMaxX Div 4);
FillEllipse (150,150,300,75).
119
Reprezentarea vizuală a datelor
120
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Aşa după cum s-a mai remarcat, nu toate procedurile grafice ţin seama de
setarea modului de scriere. Rutinele care determină culoarea nouă după regulile
descrise sunt:
Line LineTo LineRel OutTextXY
DrawPoly Rectangle OutText
• Salvarea şi restaurarea unei imagini. Sistemul prevede salvarea, din
memoria ecran, a unei imagini încadrată într-o zonă dreptunghiulară, într-un buffer
definit de utilizator în memoria internă, de regulă în yona heap. Se utilizează
procedura:
GetImage(X1,Y1,X2,Y2,Buffer);
în care (X1,Y1), (X2,Y2) dau coorodnatele colţurilor stânga-sus şi dreapta-jos ale
drptunghiului care defineşte imaginea de salvat. Variabila Buffer trebuie să aibă un
număr acoperitor de cuvinte pentru imagine, plus două cuvinte, (la începutul
acesteia) în care procedura înscrie lăţimea şi lungimea dreptunghiului de imagine.
Mărimea variabilei Buffer este returnată de funcţia ImageSize(X1,Y1,X2,Y2) de
tipul WORD şi nu poate depăşi 64KB. Imaginea salvată în buffer poate să fie afişată
din nou utilizând procedura:
PutImage (X1,Y1,Buffer,WriteMode);
121
Reprezentarea vizuală a datelor
Exemplu:
6.19. Se desenează un cerc umplut, cu centrul în (50,50) şi raza de 40 pixeli.
Imaginea se salvează şi apoi este afişată (restaurată) în punctul (200,50), fiind ştearsă
din vechea sa poziţie. Imaginea nouă conservă imaginea existentă pe ecran.
. . . . . . .
Uses CRT,Graph
. . . . . . .
p:Pointer;
Sz:Word;
. . . . . . . . .
PieSlice(50,50,0,360,40);
Sz:=ImageSize(0,0,50,100);
GetMem(p,Sz);
GetImage(0,0,50,100,p^);
Delay(2000);
PutImage(0,0,p^,XORPut);
PutImage(200,50,p^,XORPut);
. . . . . . . . .
• Pagina activă şi pagina video. Pentru modurile grafice care suportă mai
multe pagini (EGA, VGA etc.), imaginea se poate constitui în pagina activă, în timp
ce pe ecran este afişată pagina video (vezi §6.2). Pentru selecţia paginilor se
utilizează procedurile:
SetActivePage(NumarPagina);
SetVisualPage(NumarPagina);
în care parametrul NumarPagina, de tipul WORD, poate lua valori naturale,
începând cu zero. Facilitatea de paginare în memoria video asigură o creştere de
viteză şi uşurinţă în realizarea imaginilor, mai ales, în animaţie.
122
TEHNICI SPECIALE
ÎN PASCAL
123
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Programul principal şi
unităţile standard (30 KO)
Unitatea C (5 KO)
Unitatea A (10 KO)
124
Tehnici speciale în Pascal
Pentru exemplul din figura 7.1, pot fi concepute, principial, structurile din
figura 7.2. În cazul în care una dintre unităţile apelate conţine parte de iniţializare,
datorită restricţiei ca procedura OvrInit să se execute prima, corelat cu faptul că
partea de iniţializare a unei unităţi se lansează în execuţie cu prioritate, va trebui
construită o unitate separată, care să conţină în partea de iniţializare apelul
procedurii OvrInit şi care să fie invocată prima în clauza USES a programului
principal (această unitate va conţine USES Overlay).
a) Unităţile
{$O+,F+}
PROGRAM PP; {Apelatorul}
USES Overlay, A,B,C
{$O A}
{$O B}
……………….
BEGIN
OvrInit(‘ALFA.OVR’);
……………….
END.
b) Programul
125
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
suficient, noua unitate va fi încărcată, evitând reluarea încărcării unora dintre unităţi
de fiecare dată când sunt referite.
Dacă utilizatorul doreşte să mărească dimensiunea buffer-ului, tocmai pentru
a rezolva asemenea probleme, se poate folosi procedura OvrSetBuf, definită astfel:
OvrSetBuf(bufsize:Longint), unde bufsize precizează noua mărime a buffer-ului,
care trebuie să fie mai mare decât cea implicită, fără a depăşi valoarea returnată de
funcţia MemAvail: bufsize<=MemAvail+OvrGetBuf (MemAvail returnează volumul
total de memorie nealocată în heap). Procedura OvrClearBuf eliberează buffer-ul,
pentru utilizări în alte scopuri.
Pentru a elimina repetarea operaţiilor de I/E necesare încărcării unităţilor din
fişierul asociat structurii de reacoperire, este posibilă folosirea memoriei EMS
(Expanded Memory System), prin apelul procedurii OvrInitEMS (fără parametri). În
acest caz, se va asigura încărcarea fişierului în memoria EMS (dacă aceasta are spaţiu
suficient), preluarea unităţilor în buffer-ul structurii de reacoperire realizân-du-se
foarte rapid, direct din această memorie, fără operaţii de I/E. Dacă nu există spaţiu
suficient, procedura este inefectivă.
Execuţia procedurilor şi funcţiilor de gestiune a structurilor de reacoperire se
poate încheia cu succes sau cu eroare, situaţie care poate fi determinată pe baza
folosirii variabilei predefinite OvrResult. În unitatea Overlay sunt declarate mai
multe constante simbolice asociate unor valori ale variabilei OvrResult (anexa 5).
126
Tehnici speciale în Pascal
Exemplu:
7.1. Se consideră un program director (Monitor) care lansează în execuţie trei
programe, în cadrul unei aplicaţii multifunţionale. Cele trei programe sunt memorate
în fişierele Fis1.EXE, Fis2.EXE, Fis3.EXE.
PROGRAM Monitor;
{$M 2048,0,0}
USES CRT;
VAR
c:CHAR;
BEGIN
REPEAT
ClrScr;
WriteLn(’ Functiile realizate de program sunt:’);
WriteLn(’ ’:5,’1 - Semnificatie functia 1’);
WriteLn(’ ’:5,’2 - Semnificatie functia 2);
WriteLn(’ ’:5,’3 - Semnificatie functia 3’);
WriteLn(’ ’:5,’4 - STOP);
Write (’Tastati functia dorita [1-4] : ’); Readln( c );
CASE c OF
’1’: Exec(’Fis1’,’’);
’2’: Exec(’Fis2’,’’);
’3’: Exec(’Fis3’,’’);
’4’: WriteLn(‘<< Program terminat>>’)
ELSE
WriteLn(’***Cod eronat de functie***’)
END
UNTIL c=’4’
END.
127
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
7.2. Se consideră programul ExDir, care poate prelua din linia de comandă
directorul în care se găsesc fişierele aplicaţiei, transformându-l în director curent. Se
asigură salvarea directorului în uz de la momentul lansării, respectiv restaurarea lui la
încheierea execuţiei programului. Dacă noul director nu există, este prevăzută
posibilitatea creării şi activării lui ca director curent. În cadrul programului,
procedura OpDir asigură următoarele operaţii: determinarea directorului curent ('D'),
schimbarea directorului curent ('S'), crearea unui nou director ('C'). Procedura
returnează o variabilă de eroare (rez) când operaţiile asupra directoarelor nu se pot
executa.
PROGRAM ExDir;
VAR
r : INTEGER; c : CHAR;
dinc,dnou: STRING;
PROCEDURE OpDir(op:CHAR; VAR d:STRING; VAR rez:INTEGER);
BEGIN
CASE UpCase(op) OF
'D': GetDir(0,d);
'S': BEGIN {$I-} ChDir(d); {$I+} rez:=IOResult END;
'C': BEGIN {$I-} MkDir(d); {$I+} rez:=IOResult END
END
END;
PROCEDURE DetDir;
128
Tehnici speciale în Pascal
VAR
dir: STRING;
BEGIN
OpDir('d',dir,r);
WriteLn('Director curent: ',dir)
END;
BEGIN
WriteLn('Program in executie curenta: ',ParamStr(0));
DetDir;
IF ParamCount <> 0 THEN
BEGIN
dnou:=ParamStr(1);
OpDir('d',dinc,r);
OpDir('s',dnou,r);
IF r <> 0 THEN
BEGIN
WriteLn('Director inexistent: ',ParamStr(1));
Write('Se creeaza ca director nou?(Y/N): ');
ReadLn(c);
IF UpCase(c) = 'Y' THEN
BEGIN
OpDir('c',dnou,r);
IF r = 0 THEN
OpDir('s',dnou,r)
ELSE
BEGIN
WriteLn('Eroare la creare director. Executie
intrerupta!' );
RunError(r)
END
END
END
END;
DetDir
END; {restul programului se scrie normal}
IF ParamCount <> 0 THEN OpDir('s',dinc,r);
DetDir
END.
129
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
PROGRAM TabVectInt;
USES Dos,Crt,Hex;
VAR
i : BYTE;
adrint: POINTER;
PROCEDURE Defilare;
VAR
car: CHAR;
BEGIN
GotoXY(1,25);
Write('Press any key ...');
car:=ReadKey;
ClrScr
END;
BEGIN
ClrScr;
FOR i:=0 TO 255 DO
BEGIN
GetIntVec(i,adrint);
IF (i+1) MOD 24 = 0 THEN Defilare;
WriteLn(HexOctet(i):5,HexCuvant(Seg(adrint^)):5,
HexCuvant(Ofs(adrint^)):5)
END;
Defilare
END.
UNIT Hex;
INTERFACE
FUNCTION HexOctet(nroct:BYTE):STRING;
FUNCTION HexCuvint(nrcuv:WORD):STRING;
IMPLEMENTATION
FUNCTION CifraHex(cifra:BYTE):CHAR;
130
Tehnici speciale în Pascal
BEGIN
IF cifra < 10 THEN CifraHex:=Chr(48+cifra)
ELSE CifraHex:=Chr(55+cifra)
END;
FUNCTION HexOctet;
BEGIN
HexOctet:=CifraHex(nroct DIV 16) + CifraHex(nroct
MOD 16);
END;
FUNCTION HexCuvant;
BEGIN HexCuvint:=HexOctet(Hi(nrcuv))+HexOctet(Lo(nrcuv))
END
END.
PROGRAM Calendar_1;
USES Dos,Crt;
VAR
ac,lc,zc,zsc: WORD;
132
Tehnici speciale în Pascal
an,ln,zn,zsn: WORD;
i,nrz: BYTE;
BEGIN
REPEAT
Write('Luna, an: '); ReadLn(ln,an);
UNTIL (an > 1979) AND (an < 2100) AND (ln IN [1..12]);
zn:=1;
GetDate(ac,lc,zc,zsc);
SetDate(an,ln,zn);
GetDate(an,ln,zn,zsn);
SetDate(ac,lc,zc);
CASE ln OF
1,3,5,7,8,10,12: nrz:=31;
4,6,9,11 : nrz:=30;
2 : IF an MOD 4 =0 THEN nrz:=29 ELSE nrz:=28
END;
ClrScr;
WriteLn(' ',ln:2,' - ',an:4,#13#10);
WriteLn(' L M M J V S D');
WriteLn(' ---------------------');
IF zsn = 0 THEN zsn:=7;
FOR i:=1 TO nrz+zsn-1 DO
BEGIN
IF i <= zsn-1 THEN Write(' ')
ELSE Write(i-zsn+1:3);
IF i MOD 7 = 0 THEN WriteLn
END;
WriteLn;
WriteLn(' ---------------------')
END.
reg1.AH:=$2A; reg1.AH:=$2A;
MsDos(reg1); MsDos(reg1);
ac:=reg1.CX; zsn:=reg1.AL;
lc:=reg1.DH; reg2.AH:=$2B;
zc:=reg1.DL; reg2.CX:=ac;
zsc:=reg1.AL; reg2.DH:=lc;
reg2.AH:=$2B; reg2.DL:=zc;
reg2.CX:=an; MsDos(reg2);
reg2.DH:=ln;
reg2.DL:=zn;
MsDos(reg2);
133
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
7.3. Fiind dat un vector de mari dimensiuni, memorat într-un fişier binar, să
se realizeze programul de ventilare a fişierului cu obţinerea a două fişiere binare, cu
valorile pozitive, respectiv negative ale fişierului (valorile nule vor fi neglijate).
Programul Ventilare realizează încheierea execuţiei cu apelul procedurii Halt, care,
134
Tehnici speciale în Pascal
PROGRAM Ventilare;
VAR
fi,fp,fn: FILE OF INTEGER;
v : INTEGER;
np,nn:WORD;
BEGIN
Assign(fi,'FI.DAT'); Assign(fp,'FP.DAT');
Assign(fn,'FN.DAT');
Reset(fi); Rewrite(fp); Rewrite(fn);
WHILE NOT Eof(fi) DO
BEGIN
Read(fi,v);
IF v > 0 THEN Write(fp,v)
ELSE IF v < 0 THEN Write(fn,v)
END;
np:=FileSize(fp); nn:=FileSize(fn);
IF (np <> 0) AND (nn <> 0)
THEN Halt {0 - ambele fisiere nevide}
ELSE IF np <> 0
THEN Halt(1) {1 - fisierul FN vid}
ELSE IF nn <> 0
THEN Halt(2) {2 - fisierul FP vid}
ELSE Halt(3) {3 - ambele fisiere vide}
END.
PROGRAM GestProc;
USES Dos;
{$M 2048,0,0}
VAR
fi : FILE OF INTEGER;
vi,n,i: INTEGER;
r : WORD;
BEGIN
Write('Numar de elemente si valoare initiala:');
ReadLn(vi,n);
Assign(fi,'FI.DAT');
Rewrite(fi);
FOR i:=vi TO vi+n-1 DO Write(fi,i);
Close(fi);
SwapVectors;
Exec('VENT','');
SwapVectors;
r:=DosExitCode;
IF r = 0 THEN
WriteLn('OK!')
ELSE IF r = 1 THEN
135
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
WriteLn('Fisierul FN vid')
ELSE IF r = 2 THEN
WriteLn('Fisierul FP vid')
ELSE
WriteLn('Ambele fisiere vide')
END.
Exemplu:
7.4. În programul Valid se realizează introducerea cu validare a unei valori
numerice, cu reluare până la furnizarea unei valori corecte, respectiv până la
depăşirea unui număr limită de reluări.
PROGRAM Valid;
VAR n,i,er : BYTE; x : REAL;
BEGIN
Write('Numar maxim admis de reluari:'); ReadLn(n); i:=0;
REPEAT
er:=0;
Write('x:'); {$I-} ReadLn(x); {$I+}
IF IOResult <> 0 THEN
BEGIN
Write('Valoare nenumerica');
er:=1
END;
Inc(i)
UNTIL (er = 0) OR (i > n);
IF i > n THEN
BEGIN
Write('Depasire numar de reluari admis');
RunError(106);
END
END.
136
Tehnici speciale în Pascal
PROGRAM Ex_ExitProgram;
VAR
ExitOrig: POINTER;
{F+}
PROCEDURE ExitPropriu;
{$F-}
BEGIN
{Instructiuni ale utilizatorului la terminarea programului }
WriteLn('Terminarea programului...');
ExitProc:= ExitOrig; {restaurarea adresei rutinei implicite
de incheiere a executiei}
END;
BEGIN
ExitOrig:=ExitProc; {salvarea adresei rutinei implicite de
incheiere a executiei}
ExitProc:=@ExitPropriu; {includerea rutinei proprii de
incheiere la inceputul lantului de apeluri}
{Restul programului se scrie normal}
END.
137
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
VAR
pret_unitar: REAL
BEGIN
absenţa caracterului ";" de încheiere a declaraţiei variabilei se sesizează doar la
începutul liniei următoare, mesajul de eroare fiind: Error 85: ";" expected, iar
cursorul se va plasa la începutul cuvântului BEGIN.
Pentru o instrucţiune de forma:
IF a > b THEN WriteLn(a,' > ',b);
ELSE WriteLn(a,' <= ',b);
mesajul de eroare este: Error 113: Error in statement. Cauza erorii este folosirea
incorectă a terminatorului ";" la încheierea ramurii THEN a instrucţiunii IF, iar
eroarea este sesizată la întâlnirea lui ELSE, cu plasarea cursorului la începutul
138
Tehnici speciale în Pascal
139
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
140
Tehnici speciale în Pascal
141
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
142
PRELUCRAREA ETICHETELOR
DE FIŞIER ŞI A ALTOR INFORMAŢII
EXTERNE MEMORATE ÎN DISCURI
Un disc magnetic, în structură DOS, este compus din următoarele părţi: tabele
de alocare a fişierelor, tabela directorului rădăcină, zona de date a fişierelor. În plus,
discul sistem mai conţine zona programului de încărcare a sistemului de operare
(BOOT), localizată pe primul sector al primei piste. Fiecare fişier (subdirector) are o
intrare (tip etichetă) în directorul (subdirectorul) părinte.
♦ Tabela de alocare a fişierelor (FAT) este împărţită în câmpuri care co-
respund cluster-elor de pe disc. Cluster-ul este format din unul sau mai multe sectoare
şi reprezintă unitatea de alocare pe disc. Numărul de sectoare ale unui cluster este
memorat în blocul de parametri ai BIOS şi diferă de la un tip de disc la altul (flexibil
sau Winchester) şi de la un tip de calculator la altul. Oricum, el este o putere a lui doi.
Exemplu:
8.1. Disc flexibil simplă faţă - 20 sectoare/cluster; disc flexibil dublă faţă - 21 sec-
toare/cluster; disc Winchester pentru PC/AT - 22 sectoare/cluster; disc Winchester
pentru PC/XT - 23 sectoare/cluster.
143
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
Câmpurile din FAT au 16 biţi (un cuvânt), prin care se pot adresa 216 clustere
(la unele sisteme, câmpurile din FAT au 12 biţi).
Primul cuvânt din FAT (cu numărul zero) conţine identificatorul tabelei de
alocare, numit şi descriptorul suportului. El indică, codificat, tipul de disc utilizat. Al
doilea cuvânt din FAT (cu numărul unu) conţine FFFF16. Începând de la cuvântul cu
numărul doi, sunt memorate lanţuri de alocare pentru fiecare fişier. Un cuvânt din FAT
poate avea următorul conţinut:
000016 - cluster-ul corespunzător este disponibil;
FFF016-FFF616 - cluster-ul corespunzător este rezervat;
FFF716 - cluster-ul corespunzător este defect;
FFF816-FFFF16 - sfârşitul lanţului de alocare;
000216-FFEF16 - adresa următorului cuvânt din FAT, corespunzător următorului clus-
ter ocupat de datele fişierului.
Lanţul de legături este realizat prin aceea că fiecare cuvânt indică numărul
următorului cuvânt din lanţ. Fişierul nu ocupă o zonă continuă în disc. El are cel puţin
un cluster (când are un singur cluster, cuvântul corespunzător din FAT este FFFF16).
Exemplu:
8.2. În figura 8.1 se prezintă o tabelă de alocare şi lanţul de legături realizat
pentru un fişier, Fis.
Pentru fişierul Fis sunt alocate cluster-ele 09-0C, 14-16, 18-19. Cluster-ul 17
este defect. Cluster-ele 0D-13, 1A-1F sunt disponibile. Un alt lanţ începe cu cluster-ul
02 şi se termină cu cluster-ul 07.
Prin algoritmi corespunzători, MS-DOS face conversia de la adresă de cluster
la adresă de sector. Pentru a evita pierderea informaţiilor, în cazul deteriorării unor
câmpuri, în disc sunt păstrate două exemplare (adiacente) din FAT.
♦ Zona tabelei directorului rădăcină (DIR) urmează, în suport, tabelei de
alocare a fişierelor şi conţine câte o intrare pentru fiecare fişier sau subdirector
descendent. Intrarea este o zonă de 32 octeţi cu o structură prestabilită. Fiecare disc are
144
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
un singur director rădăcină, căruia îi este asociată zona DIR. Dimensiunea şi poziţia
tabelei DIR sunt fixe, prestabilite prin comanda FORMAT, în momentul formatării
discului. Numărul de intrări în DIR şi poziţia tabelei în disc sunt memorate în blocul de
parametri ai BIOS-ului. Dacă discul este de sistem, primele două intrări din DIR se
referă la fişiere care conţin sistemul de operare (aceste fişiere sunt încărcate în memoria
principală de către încărcător, care se află în sectorul zero al discului).
♦ Zona de date a fişierelor (FILE) conţine partea de date a acestora şi
subdirectoarele. Subdirectoarele sunt tratate ca fişiere, cu articole speciale, de câte
32 octeţi. Structura intrărilor în subdirectoare este identică cu cea a intrărilor în director.
Numărul intrărilor în subdirectoare este nelimitat (spre deosebire de intrările în
director). Sectoarele din zona de date sunt grupate în clustere care au câte un cuvânt
corespondent în FAT. Când un fişier (subdirector) este extins, se caută în FAT clustere
libere (cuvinte cu conţinutul 000016) şi se prelungeşte lanţul de alocare. Dacă discul este
de sistem, fişierele IO.SYS şi MSDOS.SYS sunt primele în zona de date.
145
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
00 - 07 Numele fişierului
08 - 0A Extensia fişierului
0B Atributele fişierului
0C - 15 Octeţi rezervaţi
16 - 17 Ora ultimei scrieri în fişier
♦ Atributele fişierului au semnificaţia din tabelul 8.2. Prin setarea sau şter-
gerea corespunzătoare a biţilor octetului 0B pot fi obţinute şi alte combinaţii de atribute
(cea mai utilizată este valoarea 0316, care ar corespunde unui fişier readonly ascuns).
Însumarea tuturor valorilor (3F16) desemnează un fişier oarecare (orice fişier).
♦ Ora ultimei scrieri în fişier este memorată pe doi octeţi şi are structura din
figura 8.2, unde:
O reprezintă ora, cu valori binare cuprinse în intervalul [0,23];
M reprezintă minutul, cu valori binare cuprinse în intervalul [0,59];
S reprezintă numărul de incremente de două secunde, exprimat în binar.
♦ Data ultimei scrieri în fişier este memorată pe doi octeţi şi are structura din
figura 8.3, unde:
146
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
Valoarea octetului
0B Semnificaţie
Biţi Hexa
76543210
147
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
ZONA DIR
D1
AFAT
Zona de
Da te
AFAT
148
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
În unit-ul Dos sunt definite proceduri pentru poziţionarea sau citirea atributelor
unui fişier. Corespunzător valorilor pe care le poate lua octetul 0B din intrarea unui
fişier în [sub]director, în unit-ul Dos sunt definite următoarele constante:
ReadOnly = $01 ¾ fişier numai pentru citire;
Hidden = $02 ¾ fişier ascuns;
SysFile = $04 ¾ fişier de sistem;
Volumid = $08 ¾ identificator de volum;
Directory = $10 ¾ [sub]director;
Archive = $20 ¾ fişier arhivă;
AnyFile = $3F ¾ orice fişier
Fişierelor de date li se pot asocia atributele $01, $02, $20. În Turbo Pascal, un
fişier deschis are, în mod implicit, atributul $20 (arhivă) deoarece în el s-a scris sau
urmează să se scrie un articol. Fişierele binare cu atributul $01 pot fi prelucrate numai
dacă sunt deschise cu Reset şi dacă variabilei FileMode din unit-ul System i se
atribuie, în program, valoarea zero. Fişierele TEXT cu atributul $01 pot fi doar citite
(suportă numai deschiderea Reset).
Setarea pe o anumită valoare înseamnă, uneori, cumularea de atribute. De
exemplu, dacă unui fişier cu atributul $10 (subdirector) i se setează valoarea
2 (hidden), se obţine un subdirector ascuns (atributul $18).
♦ Procedura GetFAttr returnează atributele unui fişier. Ea este definită astfel:
GetFAttr(VAR f; VAR attr:WORD)
F este variabila care indică fişierul, iar attr este variabila în care procedura
depune valoarea atributelor.
Exemplu:
8.3. În secvenţa care urmează se citesc atributele fişierului extern 'TEST.DAT'
şi se afişează valorile acestora:
USES Dos;
VAR
f:TEXT;
i:BYTE;
BEGIN
.............................
Assign(f,'TEST.DAT');
GetFAttr(f,i);
Writeln(i);
Reset(f);
.............................
♦ Procedura SetFAttr poziţionează (setează) atributele unui fişier. Ea este
149
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
definită astfel:
SetFAttr(VAR f; VAR attr:WORD)
F este variabila care indică fişierul, iar argumentul real corespunzător lui attr
este o expresie a cărei valoare reprezintă atributele şi care va fi înscrisă extern în
intrarea ataşată fişierului în [sub]director.
Exemple:
8.4. În secvenţa care urmează, fişierului TEST1.DAT i se asociază atributul
Readonly, iar fişierului TEST2.DAT atributul Hidden:
USES Dos;
VAR
f1:TEXT;
f2:FILE OF READ;
BEGIN
........................
Assign(f1,'TEST1.DAT');
SetFAttr(f1,1); Reset(f1);
........................
Assign(f2,'TEST2.DAT');
SetFAttr(f2,Hidden); Reset(f2);
.......................
USES Dos;
VAR
f:FILE;
nume_dir:STRING[79];
BEGIN
Write('Director [n:\cale\director] : ');
Readln(nume_dir);
Assign(f,nume_dir);
SetFAttr(f,2);
END.
În unit-ul Dos sunt definite proceduri pentru poziţionarea sau citirea datei şi
orei ultimei scrieri în fişier. Procedurile prelucreză câmpurile $16-$17 şi $18-$19 din
eticheta fişierului (intrarea lui în [sub]director), ca o singură valoare de tip LONGINT.
"Împachetarea", respectiv "despachetarea" în/din LONGINT se pot realiza cu
procedurile PackTime, respectiv UnpackTime, definite în unit-ul Dos. Aceste
proceduri utilizează tipul de date DateTime predefinit în unit-ul Dos, astfel:
150
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
DateTime = RECORD
Year : WORD;
Month : WORD;
Day : WORD;
Hour : WORD;
Min : WORD;
Sec : WORD;
END;
♦ Procedura PackTime "împachetează" un articol de tip DateTime într-o dată
de tip LONGINT. Ea este definită astfel:
PackTime(VAR Dc:DateTime; VAR l:LONGINT)
Dc este data şi ora, exprimate pe componente, care urmează să fie
"împachetate" sub formă LONGINT în zona Dl.
♦ Procedura UnpackTime "despachetează" o valoare de tip DateTime. Ea
este definită astfel:
UnpackTime(Dl:LONGINT, VAR Dc:DateTime)
Dl este expresie de tip LONGINT care va fi "despachetată" pe componentele
datei şi orei în zona Dc.
♦ Procedura GetFTime returnează data şi ora ultimei scrieri într-un fişier. Ea
este definită astfel:
GetFTime(VAR f; VAR Dl:LONGINT)
F este variabila care indică fişierul, iar Dl este variabila în care se recepţio-
nează data şi ora, sub formă "împachetată". Despachetarea lui Dl se face cu procedura
UnpackTime.
Exemplu:
8.6.
USES Dos;
VAR
f1: FILE OF REAL
z: DateTime;
data_ora: LONGINT;
BEGIN
.....................
Assign(f1,'A:TEST.DAT');
GetFTime(f1, Data_ora);
UnpackTime(data_ora,z);
IF Z.Year < 1994
THEN Writeln('>> Varianta veche de fisier')
ELSE Writeln('>> Varianta OK');
151
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
Exemplu:
8.7.
USES Dos;
VAR
f1:FILE OF REAL;
z:DateTime; data_ora:LONGINT;
BEGIN
........................
ASSIGN (f1,'A:TEST.DAT');
z.Year:=1993; z.Month:=10; z.Day:=28;
z.Hour:=11; z.Min:=20; z.Sec:=0;
PackTime(z,Data_ora); SetFTime(f1,Data_ora);
..........................
În unit-ul System este definită procedura Erase care realizează ştergerea unui
fişier existent (şterge eticheta din director). Declaraţia ei este:
Erase(VAR f)
Dacă fişierul extern, asignat lui f, nu există, execuţia procedurii generează
eroare de I/E (IOResult are valoare diferită de zero). Procedura Erase nu se poate
executa asupra unui fişier deschis.
Exemplu:
8.8. Se citesc 100 de numere x. Elementele pozitive sunt memorate în fişierul
cu tip VECTOR.DAT. Dacă toate numerele sunt negative, fişierul VECTOR.DAT nu
trebuie să existe.
VAR
Fis:FILE OF REAL;
x:REAL; i,j:1..100; s:BOOLEAN;
BEGIN
Assign(Fis,'VECTOR.DAT'); Rewrite(Fis);
j:=FALSE;
FOR i:=1 TO 100 DO
BEGIN
Readln(x)
IF x[i] >= 0 THEN
BEGIN
Write(Fis,x);
j:=TRUE;
END;
END;
Close(Fis);
IF NOT j THEN Erase(Fis);
END.
152
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
153
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
Exemple:
8.9. Dacă unitatea C: ar avea structura de directori din figura 1.4 şi dacă
directorul curent este D4, atunci procedura GetDir(3,Cale) ar returna, în variabila Cale,
şirul: C:\D2\D4;
8.10. Procedura GetDir (9,Cale) returnează, în variabila Cale, şirul I:\, chiar
dacă unitatea I:\ nu a fost definită pe discul fix.
Exemplu :
8.11. Dacă în structura de directoare din figura 1.4, directorul curent este D4 şi
se doreşte ca D3 să devină curent, se poate proceda astfel:
ChDir('C:\D3'); sau
ChDir('\D3'); sau
ChDir('..'); ChDir('..'); ChDir('D3');
Când calea este specificată greşit se generează eroare (path not found).
Procedura are funcţie echivalentă cu comanda CHDIR din DOS.
♦ Procedura MkDir creează un director în disc. Ea are următoarea declaraţie:
MkDir(s:STRING)
S conţine un şir de forma [n:]cale care defineşte subdirectorul care se creează.
Lungimea maximă a căii, de la rădăcină până la nivelul dorit, nu trebuie să depăşească
63 caractere, inclusiv caracterele \. Calea poate fi precizată complet, pornindu-se de la
rădăcină sau relativ, folosindu-se formele prescurtate construite cu caracterele \, .,
..(vezi ChDir). Procedura are funcţie echivalentă cu comanda MKDIR din DOS.
Exemplu:
8.12. Dacă în structura de director din figura 1.4 se doreşte crearea unui
subdirector D6, subordonat lui D3, se poate proceda astfel:
MkDir('C:\D3\D6') ¾ de oriunde;
MkDir('\D3\D6') ¾ de oriunde din unitatea C;
MkDir('D6') ¾ din D3;
154
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
Exemplu:
8.13. Se propune o procedură care asigură realizarea următoarelor operaţii, în
funcţie de valoarea unui parametru (op): determinarea directorului curent, schimbarea
directorului curent, crearea unui director. Procedura se construieşte în cadrul unui unit
(GestDir), care poate fi extins şi cu alte proceduri de interes general în lucrul cu
directoare şi/sau fişiere.
UNIT GestDir;
INTERFACE
PROCEDURE OpDir(op:CHAR; VAR d:STRING; VAR rez:INTEGER);
IMPLEMENTATION
PROCEDURE OpDir;
BEGIN
CASE UpCase(op) OF
'D': GetDir(0,d);
'S': BEGIN
{$I-} ChDir(d); {$I+}
rez:=IOResult;
END;
'C': BEGIN
{$I-} MkDir(d); {$I+}
rez:=IOResult;
END;
END;
END;
END.
155
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
În unit-ul Dos sunt definite proceduri care caută un fişier într-un [sub]director
sau într-o listă de [sub]directore. Procedurile utilizează tipurile de data PathStr şi
SearchRec, definite în unit-ul Dos, astfel:
PathStr=STRING[79];
SearchRec = RECORD
Fill : ARRAY[1..21] OF BYTE;
Attr : BYTE;
Time: LONGINT;
Size : LONGINT;
156
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
Name : STRING[12];
END;
Semnificaţia câmpurilor tipului SearchRec este următoarea:
Fill: rezervat pentru sistemul de operare. Câmpul nu trebuie modificat în
program.
Attr: atributele fişierului (valoarea octetului $0B din intrarea fişierului în
[sub]director).
Time: data şi ora ultimei scrieri în fişier, sub formă împachetată (valoarea, ca
LONGINT, a octeţilor $16-$19 din intrarea fişierului în [sub]director).
Size: lungimea, în octeţi, a fişierului (valoarea câmpului $1C-$20 din intrarea
fişierului în [sub]director).
Name: numele fişierului urmat de punct şi extensia lui (valoarea câmpurilor
$0-$7 şi $8-$A din intrarea fişierului în [sub]director).
♦ Procedura FindFirst caută într-un [sub]director prima intrare a unui fişier
care are specificatorul şi atributul precizate în lista de parametri. Ea are declararea:
FindFirst(F_extern:PathStr; Attr:Word; VAR zona:SearchRec)
F_extern este specificatorul extern al fişierului care este căutat. Specificatorul
este format din cale, nume fişier şi, eventual, extensie. Când calea lipseşte se presupune
[sub]directorul curent. Numele şi extensia fişierului pot fi globale (formate cu
caracterele * sau ?). Attr reprezintă valoarea atributelor fişierului care se caută. Zona
este o variabilă de tipul SearchRec, definit în unit-ul Dos, care va conţine informaţii
despre fişierul f_extern, în cazul în care a fost găsit. Dacă f_extern nu este găsit,
variabila zona rămâne nemodificată. Când f_extern este găsit, variabila DosError
(definită în unit-ul Dos) va avea valoarea zero. În caz contrar, DosError are valoare
diferită de zero (vezi §8.3).
♦ Procedura FindNext caută într-un [sub]director următoarea intrare a unui
fişier care are specificatorul şi atributul precizate la apelul anterior al procedurii
FindFirst. De aici rezultă faptul că procedura FindNext poate fi utilizată numai dacă,
anterior, a fost apelată procedura FindFirst.
Procedura este definită astfel:
FindNext(VAR zona:SearchRec)
Zona are aceeaşi semnificaţie ca la procedura FindFirst. Dacă următoarea
intrare este găsită, variabila DosError (definită în unit-ul Dos) va avea valoarea zero.
În caz contrar, DosError are valoare diferită de zero.
Exemplu:
8.14. Următorul program afişează toate fişierele cu extensia .DAT dintr-o cale
precizată de la tastatură. Pentru fiecare fişier se afişează: numele, extensia, atributul,
lungimea, data şi ora ultimei scrieri în fişier.
PROGRAM EX14;
USES Dos;
VAR
Cale:String[79];
Zona:SearchRec; {Tip definit in Dos}
157
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
Exemplu :
8.15. Programul care urmează caută un fişier într-o listă de directori. Când
fişierul este găsit se afişează specificatorul extern. În caz contrar, se afişează mesajul
>>Fişier negăsit.
PROGRAM EX15;
USES Dos;
VAR
Nume:PathStr;
x:STRING;
BEGIN
Write('Numele extern al fisierului cautat (xxxxxxxx.eee):');
Readln(Nume);
x:=FSearch(Nume,'C:\wp51\;\wp51\rosca\;\tp\');
IF x[0] = #0
THEN Writeln('>>Fisier negasit')
ELSE Writeln('>>Fisierul are specificatorul extern : ',x)
END.
158
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
8.20. Programul care urmează caută un fişier într-o listă de directori. Când
fişierul este găsit se afişează specificatorul extern. În caz contrar se afişează mesajul
>>Fişier negăsit.
Deosebirea faţă de exerciţiul 15 constă în aceea că, în cazul în care fişierul este
găsit în directorul curent, se scrie specificatorul complet (n:\cale\nume_fişier.extensie).
PROGRAM EX20;
USES Dos;
VAR
Nume:PathStr;
x:STRING;
BEGIN
Write('Numele extern al fisierului cautat (xxxxxxxx.eee):');
Readln(Nume);
x:=FSearch(Nume,'C:\wp51\;\wp51\rosca\;\tp\');
159
Programarea calculatoarelor– Tehnica programării în limbajul Pascal
IF x[0] = #0
THEN Writeln('>>Fisier negasit')
ELSE Writeln('>>Fisierul are specificatorul extern :
',FExpand(x))
END.
Exemplu:
8.21. Fie un program care lucrează cu un fişier al cărui nume este introdus de la
tastatură. În cazul în care de la tastatură se introduce <ENTER>, se lucrează cu fişierul
implicit EX.DAT.
PROGRAM EX21;
USES Dos;
VAR
f:FILE OF REAL;
spec:PathStr; u:DirStr;
n:NameStr; e:ExtStr;
BEGIN
Write('Nume fi•ier (EX.DAT):');
Readln(Spec);
FSplit(Spec, u, n, e);
IF n = '' THEN n:='Ex';
IF e = '' THEN e:='.DAT';
Spec:=u+n+e;
Assign(f, spec);
Reset(f);
{Prelucrare}
Close (f);
END.
În unit-ul Dos există definite două funcţii care returnează capacitatea unui disc
şi spaţiul neocupat din el. Dacă funcţiile se aplică unităţilor de disc flexibil, în cazul în
care nu este montat discul, se generează un mesaj de sistem care solicită acest lucru
(afirmaţia este valabilă pentru toate funcţiile şi procedurile Pascal care referă unităţi de
discuri flexibile).
160
Prelucrarea etichetelor de fişier şi a altor informaţii externe memorate în discuri
Exemplu:
8.22. Se presupune că urmează să fie creat un fişier, MAT.DAT, care are
aproximativ 10000 octeţi. Se poate testa, cu o oarecare aproximare, dacă fişierul încape
pe un anumit disc (fie el C:). Trebuie reţinut faptul că octeţii liberi sunt grupaţi în
clustere libere.
USES Crt;
.........
BEGIN
.........
IF DiskFree(3) = -1 THEN
Writeln('>>Unitatea C defecta')
ELSE
IF DiskFree(3) > 10000 THEN
{Asignare, deschidere, creare}
ELSE
Writeln('>>Spatiu insuficient pentru fisier');
..........
161
UNELE ASPECTE TEHNICE
REFERITOARE LA PRELUCRAREA
FIŞIERELOR
162
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
buffer etc., astfel încât, la un moment dat, buffer-ele vor conţine cele mai recente date
citite. Dacă citirea se face aleator (nu secvenţial), se încarcă în buffer(e)
sectorul/sectoarele (ca întregi) care conţin(e) articolul. Dacă articolul este deja în
buffer(e), nu are loc transfer din exterior ci numai din buffer(e) în zonă utilizator.
Succesiunea de principiu a operaţiilor, în cazul unui flux general de date care
implică un singur buffer, este prezentată în figura 9.1. Ea este următoarea:
1. citirea unui sector din fişierul de intrare în zonă tampon asociată;
2. transferul datelor din buffer în zonă utilizator asociată fişierului de intrare;
3. pregătirea conţinutului zonei utilizatorului asociată fişierului de ieşire, pe baza
datelor preluate din zona fişierului de intrare sau din alte surse.
În limbajul PASCAL, aceeaşi zonă utilizator poate fi folosită atât pentru fişierul
de intrare, cât şi pentru cel de ieşire;
4. Transferul datelor din zona utilizator în buffer-ul fişierului de ieşire;
5. Scrierea în fişierul de ieşire a sectorului (când este completat), din zona
tampon.
Cu toate că procesul de trecere dintre nivelurile fizic şi logic are trăsături
principale comune, există deosebiri esenţiale de realizare pentru fişierele TEXT şi cele
binare. Pentru fişierele binare (cu tip şi fără tip) se poate considera valabilă schema de
principiu din figura 9.1. Transferul intern dintre buffer şi zonă utilizator (operaţiile 2 şi 4)
are loc fără conversii, iar operaţiile de intrare/ieşire dintr-un program pot avea loc pe
acelaşi fişier. Pentru fişierele TEXT, o prima particularitate constă în aceea că datele
sunt transferate în/din una sau mai multe zone de memorie independente şi neomogene
ca tip (figura 9.2).
163
Unele aspecte tehnice referitoare la prelucrarea fişierelor
În plus, datele sunt "decupate" din zona buffer în zonele de date, pe baza unor
caractere cu rol de separator sau după alte reguli. Pentru datele numerice (întregi şi
reale), transferul din/în zonele buffer în/din zonele de date (operaţiile 2 şi 4 din figura
9.2) are loc cu conversie. Acelaşi lucru se întâmplă la scriere şi cu datele de tip
BOOLEAN.
Atât pentru fişierele binare cât şi pentru cele TEXT, operaţiile de transfer din
exterior în buffer (1) şi din acesta în zona utilizator (2) au loc, de regulă, ca urmare a
execuţiei procedurilor de citire. În unele situaţii, operaţia (1) are loc ca urmare a
execuţiei funcţiilor de testare a sfârşitului de fişier. Operaţiile de transfer din zona
utilizator în buffer (4) şi din acesta în exterior (5) au loc ca urmare a execuţiei
procedurilor de scriere.
Operaţiile 1 şi 2 (respectiv 4 şi 5) nu au loc, întotdeauna, simultan. Operaţia 1
are loc numai când buffer-ul de intrare este "eliberat", iar operaţia 5 are loc numai când
buffer-ul de ieşire este plin. Procesul se desfăşoară diferit la fişierele binare şi la cele
TEXT.
♦ La fişiere binare buffer-ul de intrare este "eliberat" ca urmare a execuţiei
unei operaţii de deschidere sau a citirii unui articol/bloc (Read, BlockRead) din fişierul
respectiv.
Exemplu:
9. 1. RESET(fis); ¾ buffer "eliberat"
..................
Read(fis,art); ¾ realizeaz• opera•iile 1 •i 2 din
figura 9.1 (buffer "eliberat")
164
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Funcţia Eof găseşte întotdeauna buffer-ul "eliberat" (fie că este înaintea unui
Read, fie că este după acesta), ceea ce determină ca funcţia să efectueze transferul din
exterior în zonă buffer (operaţia 1 din figura 9.1). Când procedura Read se execută după
apelul funcţiei Eof, atunci ea realizează numai operaţia (2).
Exemplu:
9.2.
WHILE NOT Eof(f) DO¾ Transfera din fi•ier în zon• buffer
(opera•ia 1)
BEGIN
Read(F,art);¾ Transfera din buffer în zon• articol
(opera•ia 2)
(* Prelucrare articol *)
END;
Exemple:
9.3. Se presupun trei variabile a, b, c, de tip numeric şi secvenţa de program:
165
Unele aspecte tehnice referitoare la prelucrarea fişierelor
Tot procedura Read(a) realizează transferul (cu conversie) din buffer în zona a
(a=12). Pointerul se plasează în poziţia 1. Procedura Read(b) găseşte bufferul
"neeliberat", deci nu execută transfer din exterior, ci numai din buffer în zona b (b=300).
Pointerul se plasează în poziţia 2. Procedura Read(c) găseşte buffer-ul "neeliberat", deci
nu execută transfer din exterior, ci analizează caracterele din buffer. Cum la citirea
datelor numerice spaţiile şi caracterele CR/LF sunt ignorate, pointerul avansează până în
poziţia 3, când buffer-ul devine "eliberat", producându-se în acest moment o cerere de
transfer din exterior în buffer etc. În concluzie, secvenţa anterioară realizează:
Read(a) ¾ opera•iile 1 •i 2;
Read(b) ¾ opera•ia 2;
Read(c) ¾ analiza, opera•ia 1 etc..
Tot procedura ReadLn(a) transferă (cu conversie) din buffer în zona a (a=12) şi
plasează pointerul la sfârşitul liniei (în poziţia 1), după CR/LF.
Procedura ReadLn(b) găseşte buffer-ul "eliberat" şi provoacă cerere de transfer
din exterior etc.
Funcţia Eof poate găsi buffer-ul "eliberat" sau "neeliberat". Când îl găseşte
"eliberat" produce un transfer din exterior în buffer, plasează pointerul pe începutul său
şi testează dacă este sfârşit de fişier (CTRL/Z). Dacă funcţia Eof găseşte buffer-ul
"neeliberat", testează dacă pointerul indică sfârşitul de fişier. După testarea sfârşitului de
fişier, funcţia Eof nu modifică pointerul .
Eliberarea buffer-ului unui fişier TEXT, cu scrierea conţinutului său în suportul
extern, se poate realiza cu procedura Flush(f). F trebuie deschis pentru scriere.
În concluzie, atât pentru fişierele binare, cât şi pentru cele TEXT, trebuie
reţinute următoarele observaţii importante: • nu întotdeauna operaţiile Read, ReadLn,
166
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
BlockRead produc transfer din exterior în memoria principală. Transferul are loc numai
dacă buffer-ul este "eliberat"; • funcţia Eof realizează şi operaţia de transfer din exterior
în memoria principală. Acest lucru se întâmplă când, la execuţia funcţiei, buffer-ul de
intrare este "eliberat".
167
Unele aspecte tehnice referitoare la prelucrarea fişierelor
OpenFunc : Pointer;
InOutFunc : Pointer;
FlushFunc : Pointer;
UserData : ARRAY[1...16] OF BYTE;
Name : ARRAY[0...79] OF CHAR;
Buffer : TextBuf;
Semnificaţia principalelor câmpuri este următoarea:
• Handle este o valoare întreagă care reprezintă identificatorul pentru fişiere
deschise. Valorile 1-7 sunt utilizate pentru dispozitivele standard de I/E (intrare, ieşire,
auxiliar, imprimantă, fişierele în curs de folosire cu comanda PRINT din DOS şi de
reţea). Fişierele deschise ale utilizatorului au valori pentru handle începând cu 8.
Fişierele nedeschise au handle=0. O valoare handle asociată unui fişier devine
disponibilă la închiderea acestuia.
Exemple:
9.5. Dacă într-un program se lucrează simultan cu trei fişiere, valorile handle
asociate sunt 8, 9, 10.
9.6. Dacă într-un program se lucrează cu trei fişiere deschise şi închise pe rând,
fiecare va avea valoarea handle opt.
168
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
• Name este identificatorul extern al fişierului, aşa cum este precizat de pro-
cedura Assign.
• BufSize reprezintă lungimea buffer-ului fişierului TEXT.
• BufPos, BufEnd, BufPtr reprezintă pointeri folosiţi în gestiunea buffer-elor
asociate fişierului TEXT.
• OpenFunc, InOutFunc, CloseFunc, FlushFunc reprezintă adresa
driver-elor pentru deschidere, citire/scriere, închidere, golire a buffer-ului.
• Buffer reprezintă zonă tampon asociată fişierului (chiar buffer-ul fişierului).
Tipul TextBuf este definit în unit-ul Dos astfel: TextBuf = ARRAY[0...127] of CHAR.
Pentru a avea acces la informaţiile din FIB, trebuie declarată o variabilă de tip
FileRec (respectiv TextRec) care să aibă aceeaşi adresă cu FIB-ul (cu variabila de tip
fişier):
a) Pentru fişiere TEXT:
VAR
f: TEXT; inf_f: TextRec ABSOLUTE f;
b) Pentru fişiere cu tip:
VAR
f: FILE OF tip; inf_f: FileRec ABSOLUTE f;
c) Pentru fişiere fără tip:
VAR
f: FILE; inf_f:FileRec ABSOLUTE f;
Câmpurile zonei inf_f se adresează cu denumirile lor din definirea articolului
FileRec, respectiv TextRec, din unit-ul Dos.
În fiecare dintre unit-urile standard sunt definite variabile şi funcţii care pot
semnala modul de desfăşurare a operaţiilor de intrare/ieşire.
♦ În unit-ul System sunt definite următoarele variabile şi funcţii:
• Variabila InOutRes conţine codurile de eroare la execuţie, generate de rutinele
de I/E, corespunzând unor situaţii cum sunt (anexa 3): eroare la citire de pe disc (codul
100), la scriere pe disc (101), fişier neasignat (102), fişier nedeschis (103), fişier
nedeschis pentru intrare (104), fişier nedeschis pentru ieşire (105), format numeric
invalid (106). Variabila InOutRes are valoarea zero dacă operaţia de I/E s-a desfăşurat
normal. Ea este definită astfel: InOutRes:Integer=0;
• Funcţia IOResult returnează programului valoarea variabilei InOutRes şi o
169
Unele aspecte tehnice referitoare la prelucrarea fişierelor
pune pe aceasta pe zero. Dacă valoarea InOutRes este diferită de zero şi directiva de
compilare $I are valoarea {$I-}, programul nu se întrerupe, dar următoarea operaţie de
I/E nu se va mai executa; dacă are valoarea {$I+} programul se opreşte cu eroare de
execuţie. De aceea, dacă se doreşte controlul desfăşurării unei operaţii de I/E, se
procedează astfel (pe exemplul procedurii READ):
{$I-} {inhibă întreruperea programului}
Read (f,zonă);
{$I+} {autorizează execuţia urmatoarei operaţii de I/E}
IF IOResult <>0
THEN {eroare de I/E} ELSE {nu există eroare de I/E};
• Variabila ErrorAddr conţine adresa unde s-a produs eroarea de execuţie. În
cazul inexistenţei erorii, conţine valoarea nil. Variabila este definită astfel:
ErrorAddr:pointer=nil. Adresa este memorată sub forma ssss:dddd, unde ssss este
adresa segmentului de cod, iar dddd este offset-ul (deplasarea în cadrul segmentului).
♦ În unit-ul Dos sunt definite următoarele proceduri şi variabile:
• Variabila DosError, de tip INTEGER, este iniţializată de funcţiile şi proce-
durile din acest unit, cu următoarele valori principale (vezi anexa 1):
0 - fără eroare;
2 - fişier negăsit;
3 - cale negăsită;
4 - prea multe fişiere deschise;
5 - acces interzis; etc.
Variabila DosError are valoarea zero când execuţia functiilor şi a procedurilor
definite în unit-ul Dos se termină normal.
• Procedura SetVerify poziţionează comutatorul verify din DOS. Comutatorul
are două valori posibile: ON (valoare logică TRUE) sau OFF (valoare logică FALSE).
Când comutatorul are valoarea ON, sistemul de operare face verificare după fiecare
operaţie de scriere în fişiere (se verifică dacă datele scrise pot fi citite fără eroare). Poziţia
On a comutatorului verify măreşte timpul de execuţie a programelor. Când comutatorul
are valoarea OFF, sistemul de operare nu face verificarea scrierii. Valoarea implicită a
comutatorului este OFF. Valoarea comutatorului rămâne activă până la o nouă setare.
Procedura este defintă astfel:
SetVerify(v:Boolean)
V este o variabilă de tip BOOLEAN. Când v are valoarea TRUE, comutatorul
verify primeşte valoarea ON. În caz contrar primeşte valoarea OFF. Procedura are efect
similar cu comanda VERIFY din DOS. Dacă comutatorul verify este ON, rezultatul
verificării scrierii este memorat în variabila DosError.
În anexa 3 sunt prezentate principalele erori de execuţie, care includ şi pe cele
de intrare/ieşire.
170
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Deoarece peste un fişier fizic poate fi suprapus orice tip de fişier, rămâne în
sarcina programatorului să asigure compatibilitatea cu cerinţele de prelucrare. În cazul
fişierelor binare, lungimea şi structura articolelor (blocurilor) pot diferi între momentul
creării şi cel al exploatării.
Exemplu:
9.7. La momentul creării, o matrice este scrisă câte o linie pe articol:
TYPE
a=ARRAY[1..10] of REAL;
VAR
Fis:FILE OF a;
Linie:a;
.....................
BEGIN
Assign(Fis,'MATRICE.DAT');
Rewrite(Fis);
.....................
Write(Fis,Linie);
.....................
VAR
Fis:FILE OF REAL;
Element:REAL;
.....................
BEGIN
Assign(Fis,'MATRICE.DAT');
Reset(Fis);
.....................
Read(Fis,Element);
.....................
Fie lart şi lbloc lungimea articolelor, respectiv blocurilor unui fişier binar care
are lungimea lfis, exprimată în octeţi. O prelucrare corectă a fişierului presupune să fie
îndeplinite următoarele condiţii: lfis MOD lart=0, respectiv lfis MOD lbloc=0.
În caz contrar se produc anomalii în prelucrare, ultimii lfis MOD lart, respectiv
lfis MOD lbloc octeţi, neputând fi prelucraţi.
Exemplu:
9.8. În programul demonstrativ care urmează se creează fişierul TEST.DAT care
are lungimea 8 octeţi (se scriu patru articole de câte doi octeţi). Fişierul este exploatat cu
171
Unele aspecte tehnice referitoare la prelucrarea fişierelor
articole de 6 octeţi (REAL), FileSize(f2) returnând valoarea 1 (8 DIV 6). Prima citire din
fişier transferă primii 6 octeţi. A doua citire produce eroare de I/E
(8 MOD 6 = 2).
PROGRAM Ex8A;
VAR
f1:FILE OF WORD;
x:WORD;
f2:FILE OF REAL;
y:REAL;
BEGIN
Assign(f1,'TEST.DAT'); Rewrite(f1);
x:=0;
Write(f1,x,x,x,x);
Close(f1);
Assign(f2,'TEST.DAT'); Reset(f2);
Writeln(FileSize(f2));
Read(f2,y);
Read(f2,y); {Error 100: Disk read error}
Close(f2);
END.
172
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Eof(f) nu poate returna niciodată valoarea TRUE, deoarece "articolul scurt" nu poate
fi niciodată citit. Din cele prezentate anterior, rezultă că, în cazul prelucrării unui
fişier cu structură necunoscută, este recomandabil să se lucreze cu articole de tip
CHAR sau cu blocuri de lungime unu.
173
OBIECTE ÎN PASCAL
174
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
ClasaRational
Numarator: Integer
Numitor: Integer Obiecte
AddRational (b,c) a: (5;7)
SubRational (b,c) b: (3;1)
MulRational (b,c) c: (2;5)
DivRational (b,c) d: (7;5)
OpusRational (aopus) x: (0;1)
InversRational (ainvers)
175
Obiecte în Pascal
176
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Deoarece o clasă este un tip de dată, în definirea unei clase B se pot declara
atribute de tip A, unde A este la rândul ei o clasă. Mai mult, o clasă A poate defini
atribute de tip A. De exemplu, clasa Carte din figura 10.3 are atributul Autor de
tipul Persoana care este, de asemenea, o clasă. Mai mult, Persoana are atributul
Sef, care este de acelaşi tip (Persoana).
Persoana 1
100 Persoana Carte
Ionescu Marca: Integer Cota: String
Scriitor Nume: String Titlu: String
Persoana 2 Profesia: String Autor:Persoana
Sef: Persoana Pret: Real
Persoana 2
70
Popescu
Reporter
----------
Definirea atributelor unei clase ca tipuri ale altei clase pune în evidenţă o
relaţie între clase şi, deci, între obiectele acestora.
Din punct de vedere funcţional, metodele unei clase au destinaţii diverse.
În multe cazuri şi depinzând de limbaj, unei clase i se poate defini o metodă
constructor şi o metodă destructor. Un constructor este o metodă care creează un
177
Obiecte în Pascal
Stack
Cap: Nod Partea privată
Contor: Integer
Push ( )
Pop ( ) Interfaţa(partea publică)
Top ( )
Empty ( )
178
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Point
x: Integer x: 30
y: Integer y: 150
Desenează ( )
Distanţa (p:Point): Real
Cerc
Raza: Integer x: 200
Arie ( ): Real y: 180
Desenează ( ) Raza: 50
179
Obiecte în Pascal
la care se adaugă elemente funcţionale noi; • moştenirea este mecanismul prin care
se asigură reutilizarea codului, sporind productivitatea muncii de programare.
180
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
181
Obiecte în Pascal
metoda_1;
...............
metoda_m;
{ PRIVATE
atribut_1;
...............
atribut_p;
metoda_1;
...............
metoda_q; }
END;
182
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
10.1. Declararea şi definirea unei clase
183
Obiecte în Pascal
184
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
10.2. Se declară şi se defineşte o clasă de numere raţionale ca perechi
(Numarator, Numitor) de numere întregi. O astfel de clasă poate fi utilă în calcule
cu fracţii ordinale, atunci când transformarea acestora în fracţii zecimale (numere
reale) nu este de dorit din cauza erorilor de trunchiere.
UNIT ClasaRat;
INTERFACE
Type
Rational = OBJECT
Procedure InitRat (x, y:Integer);
Procedure AddRat (b: Rational; VAR c: Rational);
Procedure SubRat (b: Rational; VAR c: Rational);
Procedure MulRat (b: Rational; VAR c: Rational);
Procedure DivRat (b: Rational; VAR c: Rational);
Procedure OpusRat (VAR c: Rational);
Procedure InversRat (VAR c: Rational);
Function GetNumarator: Integer;
Function GetNumitor: Integer;
Procedure ReadRat;
Procedure WriteRat;
Function CompRat (b: Rational): Integer;
PRIVATE
Numarator, Numitor: Integer;
Procedure SignRat;
END;
IMPLEMENTATION
Function Cmmdc (x,y: Integer ):integer:FORWARD;
Procedure Rational. InitRat (x, y: Integer);
{Initializeaza in forma inductiva functia (numarator,
numitor)}
Var d: Integer;
Begin
If (x=0) or (y=1)
then y=1
else begin
If (abs(x)<>1) and (abs(y)<>1) then
begin
d:= Cmmdc (x,y)
x:= x/d;
y:= y/d;
end;
SignRat;
End;
Numarator:= x;
Numitor:= y;
End;
Procedure Rational.AddRat (b: Rational; VAR c: Rational);
Var x, y: Integer;
Begin
x: = Numarator*b.Numarator+Numitor*b.Numarator;
185
Obiecte în Pascal
y: = Numitor*b.Numitor;
c.InitRat (x, y);
End;
Procedure Rational.SubRat (b:Rational; VAR c:Rational);
Var
r: Rational;
Begin
b. OpusRat ( r );
AddRat (r, c);
End;
Procedure Rational.MulRat (b: Rational; VAR c: Rational);
Var x, y: Integer;
Begin
x: = Numarator*b.Numarator;
y: = Numitor*b.Numitor;
c.InitRat (x, y);
End;
Procedure Rational.DivRat (b: Rational; VAR c: Rational);
Var r: Rational;
Begin
b.InversRat ( r );
MulRat (r, c);
End;
Procedure Rational.InversRat (VAR c: Rational);
Var d: Integer;
Begin
d:= Numarator;
if d=0 then d:=1;
c.Numarator: = c.Numitor;
c.Numitor: = d;
c.SignRat;
End;
Procedure Rational.OpusRat (VAR c: Rational);
Begin
c.Numarator: = - c.Numarator;
End;
186
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Begin
x: = abs (x); y: = abs (y);
While x< >y Do
if x>y then x: = x-y else y: = y-x;
Cmmdc: = x;
End;
Procedure Rational.ReadRat;
Var
txt: String [ 25];
i, x, y: Integer;
Begin
Read (txt);
i: Pos (txt, ‘/’);
If (i=0) then Begin {Numarator întreg}
Val (txt, Numarator);
Numitor: =1;
End
else
Begin
Val(Copy (txt, 1, C-1), x);
Val (Copy (txt, i, 25), y);
InitRat (x, y);
End;
End;
Procedure Rational.WriteRat;
Begin
Write (Numarator, ‘/’, Numitor);
End;
Program TestE3;
Uses ClasRat;
Var
a,b,c: Rational;
Op: Char; r: Integer;
Procedure Meniu;
Begin
WriteLn (‘ Meniu’);
WriteLn (‘A/a - Adunare’);
WriteLn (‘S/s - Scadere’);
187
Obiecte în Pascal
La fel ca în exemplul 10.1, şi în acest caz atributele sunt private, iar accesul
la valorile lor se asigură prin accesoriile GetNumarator, GetNumitor. În partea
privată apare o metodă care este utilizată numai intern (în clasă), în scopul asocierii
semnului numărului raţional la numărător. De asemenea, se remarcă prezenţa
funcţiei Cmmdc (cel mai mare divizor comun) care nu ţine de clasă, dar este
necesară implementării unor metode ale clasei.
La stabilirea numărului de parametri, în conceperea metodelor s-a avut în
vedere că un operand, eventual unicul pentru operaţii unare, este obiectul curent.
Atunci când, în contextul obiectului curent, s-a apelat o metodă care se referă la un
alt obiect, acesta este precizat explicit, (altfel s-ar considera acelaşi context). De
exemplu, în operaţiile de scădere şi împărţire se aplică o reducere la operaţiile de
adunare cu opusul, respectiv înmulţire cu inversul. Acest rezultat intermediar se
obţine prin b.OpusRat(r), respectiv b.InversRat(r), după care, în contextul definit la
apelul lui SubRat, respectiv DivRat se invocă AddRat(r,c) şi MulRat(r,c), ceea ce
188
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
De cele mai multe ori, spaţiul unui obiect este compact, constituit din
spaţiile necesare atributelor sale. În cazul în care unele atribute sunt de tip referinţă,
spaţiul obiectelor clasei respective are o componentă dinamică denumită spaţiu
extins (figura 10.6).
189
Obiecte în Pascal
190
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
a:=b
x x
y y
z z
. .
. .
. .
Referinţă Referinţă
Spaţiu extins
Exemplu:
10.3. Se ilustrează modul de utilizare a referinţelor în declararea claselor,
de realizare a constructorilor multipli şi a destructorului de spaţiu extins. Deşi
simplificat, exemplul sugerează modul în care se poate construi o clasă matrice cu
elemente de tip Rational, prin utilizarea clasei din exemplul 10.2.
UNIT ClasaMRat;
INTERFACE
uses ClasaRat;
Const
MaxLin = 10; MaxCol = MaxLin;
ZeroRat : Rational = (Numarator:0; Numitor:1);
Type
DimLin = 1..MaxLin;
DimCol = 1..MaxCol;
Matrice = array [DimLin, DimCol] of Rational;
MatriceRat = OBJECT
Constructor SetNulMat (m, n:Integer);
Constructor ReadMat;
Procedure AddMat (b:MatriceRat; VAR c:MatriceRat);
Procedure WriteMat;
Destructor FreeExtMat;
PRIVATE
191
Obiecte în Pascal
Nlin, Ncol:Integer;
PMat:^Matrice;
END;
Var
ErrMat:Integer;
IMPLEMENTATION
Constructor MatriceRat.SetNulMat (m, n:Integer);
Var i, j:Integer;
Begin
m:=Max (m, MaxLin);
n:=Max (n, MaxCol);
GetMem (Pmat, m*n*SizeOf (Rational));
For i:=1 to m Do
For j:=1 to n Do
P.Mat^[i, j]:=ZeroRat;
Nlin:= m; Ncol:=n;
End;
Constructor MatriceRat.ReadMat;
Var m, n, i, j:Integer;
Begin
Repeat
Write (‘Numărul de linii:’); ReadLn (m);
Until m<=MaxLin;
Repeat
Write (‘Numărul de coloane:’);
ReadLn (n);
Until n<=MaxCol;
WriteLn(‘Elementele rationale ale matricei, pe
linii!’);
GetMem (Pmat, m,*n*SizeOf (Rational));
For i:=1 To m Do
For j:=1 To n Do
Begin
Write (‘x(‘, i:2,’,’,j:2,’)=’);
PMat^[i,j].ReadRat;
WriteLn;
End;
Nlin:=m; Ncol:=n;
End;
Destructor MatriceRat.FreeExtMat;
Begin
FreeMem (PMat, Nlin*Ncol*SizeOf (Rational));
End;
Procedure MatriceRat.AddMat (b:MatriceRat; VAR c:MatriceRat);
Var i, j:Integer;
Begin
ErrMat:=0;
If (Nlin=b.Nlin) and (Ncol=b.Ncol)
then
For i:=1 To Nlin Do
For j:=1 To Ncol Do
PMat^[i,j].AddRat(b.PMat^[i,j],c.PMat^[i,j]);
else ErrMat:=1;
End;
Procedure MatriceRat.WriteMat;
Var i, j:Integer;
Begin
For i:=1 To Nlin Do
192
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Begin
WriteLn (‘Linia:’, i);
For j:=1 To Ncol Do
Begin
PMat^[i, j].WriteRat;
If (i<>Ncol) Write (‘;’);
End;
End;
End;
Program Test;
Uses ClasMat;
Var
A,B,C:MatriceRat;
Begin
A.ReadMat;
B.ReadMat;
A.AddMat (B,C);
If(ErrMat)then WriteLn (‘** Eroare:Dimensiuni
diferite’)
else C.WriteMat;
A.FreeExtMat;
B.FreeExtMat;
C.FreeExtMat;
ReadLn;
END.
193
Obiecte în Pascal
194
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
New(variabila_referinta);
New(variabila_referinta, apel_constructor);
Variabila_referinta := New(tip_referinta, apel_constructor);
Dispose(variabila_referinta)
Dispose(varaibila_referinta, apel_constructor);
De exemplu, dacă Init(x,y:Integer) este un constructor pentru cazul clasei
Tpoint, se poate scrie:
New (PtrP1); PtrP1^.Init (30,20);
sau
New (PtrP1, Init(30, 20));
sau
PtrP1:=New (Ppoint, Init (30, 20));
În acest mod se asigură o posibilitate în plus pentru evitarea omisiunii
apelului constructorului.
Similar stau lucrurile şi cu eliberarea spaţiului. Dacă obiectele dinamice nu
au spaţiu extins, atunci eliberarea spaţiului compact dinamic se va face prin apelul
procedurii Dispose, în prima formă; de exemplu, Dispose (PtrP1). Dacă obiectul
dinamic are spaţiul extins, atunci clasa prevede şi un destructor pentru acesta
(destructor de spaţiu extins). În acest caz se poate utiliza procedura Dispose în a
doua formă. De exemplu, dacă se presupune clasa MatriceRat (exemplul_3),
declarată în forma: MatriceRat=^TMatriceRat; TMatriceRat= OBJECT…End; şi
declaraţii de obiecte de forma: PtrA:TMatriceRat, se poate apela destructorul
FreeExtMat în forma: Dispose (PtrA, FreeExtMat). Prin acest apel se execută mai
întâi destructorul, deci are loc eliberarea spaţiului extins şi apoi se eliberează
spaţiul compact dinamic.
Referirea unui obiect dinamic are forma referinta^ şi accesul la membrii
obiectelor va avea formele:
referinta^.nume_metoda(argumente)
referinta^.atribut
Aşa cum se pot construi masive de obiecte statice, tot aşa se pot construi
astfel de structuri având ca tip de bază referinţe la obiecte dinamice. Accesul la
membrii obiectului k se va face sub forma:
numearray[k]^.nume_metoda(argumente);
numearray[k]^.atribut.
195
Obiecte în Pascal
196
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
O menţiune aparte trebuie făcută pentru cazul obiectelor dinamice ale unei
ierarhii de clase. Datorită polimorfismului, orice obiect este compatibil, la atribuire,
cu oricare obiect al unui strămoş. În aceste condiţii, o referinţă la un obiect strămoş
poate conţine adresa unui obiect descendent şi apelul unei metode, de forma:
PtrStramos^.Numemetoda ( ), introducând un element de nedeterminare în privinţa
obiectului de apel. Pentru astfel de cazuri, în limbajul Pascal s-a stabilit urmatoarea
regulă care se aplică în lipsa altor informaţii adiţionale: se apelează metoda clasei
corespunzăroare tipului referinţei, dacă o astfel de metodă există, sau metoda
corespunzătoare primului ascendent care o posedă.
Moştenire
Exemplu:
10.4. În ramura TX←TY←TZ←TW, definită în continuare, apare o
procedură supraîncărcată, p.
PX=^TX;
TX=OBJECT
Procedure p
END;
PY=^TY;
TY=OBJECT (TX)
Procedure p
END;
PZ=^TZ;
TZ=OBJECT (TY)
Procedure q
END;
PW=^TW;
TW=OBJECT (TZ)
Procedure r
END;
197
Obiecte în Pascal
..................
PtrX:=PtrW;
PtrX^.p;
se va apela procedura p a clasei TX, ghidându-se după referinţa PtrX care este
asociată acestei clase. Dacă se declară PtrZ:PZ; PtrW:PW şi se fac atribuirea şi
apelul:
..................
PtrZ:=PtrW;
PtrZ^.p;
198
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
10.5. Se presupune derivarea TA←TB←TC şi destructorul virtual D. Se
declară un masiv de referinţe la obiecte de clasă TA care se iniţializează cu adresele
unor obiecte. Se execută apoi ştergerea obiectelor dinamice, cu apelul
destructorului D, pentru a elibera spaţiul extins al acestor obiecte.
PA = ^ TA;
TA = OBJECT
........................................
Destructor D ; VIRTUAL;
End;
PB = ^ TB;
TB = OBJECT (TA)
........................................
Destructor D ; VIRTUAL;
End;
PC = ^TC;
TC = OBJECT (TB)
........................................
Destructor D; VIRTUAL;
End;
.............................................................
p:array [0..2] of PA;
.............................................................
..
p[0]: = new (PB,…); {contine obiect TB}
p[1]: = new (PC,…); {contine obiect TC}
p[2]: = new (PA,…); {contine obiect TA}
.............................................................
dispose (p[0], D); {apel destructor TB, TC}
dispose (p[1], D); {apel destructor TC, TB, TA}
dispose (p[2], D); {apel destructor TA}
199
Obiecte în Pascal
Obiect 1 Obiect 2
Atribut 1 Atribut 1
200
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Exemplu:
10.6. Lucrul cu metode virtuale
program virt;
uses crt;
type
parinte=object
a,b:word;
constructor init_p(a1,b1:word);
function suma:word;virtual;
end;
fiu=object(parinte)
c:word;
constructor init_f(a1,b1,c1:word);
function suma:word;virtual;
end;
var
p:parinte;
f:fiu;
constructor parinte.init_p;
begin
a:=a1; b:=b1;
end;
function parinte.suma:word;
begin
suma:=a+b;
end;
constructor fiu.init_f;
begin
a:=a1; b:=b1; c:=c1;
end;
function fiu.suma:word;
begin
suma:=a+b+c;
end;
procedure afis;
begin
writeln('Instanta parinte a=',p.a,' b=',p.b, ' dimensiunea
',sizeof(p));
writeln('Instanta fiu a=',f.a,' b=',f.b,' c=',f.c,'
dimensiunea ', sizeof(f));
readln;
end;
begin
clrscr;
fillchar(p,sizeof(parinte)+sizeof(fiu),#0);
afis; {1}
f.init_f(1,2,3);
afis;{2}
p:=f;
afis;{3}
p.init_p(4,5);
afis;{4}
p:=f;
afis;{5}
end.
201
Obiecte în Pascal
202
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
acela de clase de bază care grupează date şi metode comune. Din ele, prin
specializare, se pot construi clase noi, care vor poseda caracteristicile clasei pe care
o moştenesc şi, în plus, vor defini elemente (membri) specifice. Aşa de exemplu,
clasa Contur poate da naştere unei clase Linie, de obiecte de tip linie dreaptă, iar
clasa Suprafaţa conduce la clasele Dreptunghi şi Cerc, ca obiecte cu suprafaţă
specifică.
Clasele abstracte pot fi definite la diferite niveluri în arbore, fiind posibil
ca o clasă abstractă să fie părinte pentru o altă clasă abstractă mai specializată. De
regulă, o clasă abstractă este rădăcină în orice ierarhie. Deşi clasele abstracte nu se
instanţiază, este permis să se declare referinţe către acestea. Acest lucru asigură
polimorfismul şi, la limită, o referinţă de clasă rădăcină poate recepţiona adresa
oricărui obiect din ierarhie şi, în consecinţă, poate asigura tratarea dinamică a
obiectelor polimorfice.
Uneori, clasele abstracte sunt utilizate pentru a grupa clase care nu au date
comune, dar participă împreună la realizarea programelor. Aceste clase sunt atât de
generale încât, de regulă, se reduc la un constructor vid şi, eventual, la un
destructor vid şi virtual. Ele se justifică prin aceea că, în virtutea polimorfismului,
permit aplicarea unui cod comun şi general la obiectele acestor clase, cum ar fi
constituirea de liste eterogene.
Definirea unei clase abstracte drept clasă de bază a unei ierarhii conduce la
posibilitatea de a utiliza aceeaşi clasă pentru toate ierarhiile. Principiul este aplicat,
în general, la construirea bibliotecilor de clase pe care limbajele orientate obiect le
oferă. De exemplu, în Turbo Pascal, biblioteca denumită Turbo Vision are drept
clasă primordială pentru toate ierarhiile clasa TObject. Astfel se “leagă” între ele
diferitele ierarhii şi se poate utiliza facilitatea de polimorfism.
10.4.2 Definirea metodelor
203
Obiecte în Pascal
204
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Figura
x, y
EsteVizibil
Culoare
INIT
AFISEAZA
ASCUNDE
ARIE
SCHIMBACULOARE
Punct Contur
AFISEAZA TipLinie
ASCUNDE GrosimeLinie
INITDEFAULT
SCHIMBACONTUR
Linie Suprafata
XSfarsit TipHasura
YSfarsit CuloareUmplere
Init INITDEFAULT
AFISEAZA SCHIMBAUMPLERE
ASCUNDE
Dreptunghi Cerc
Latime Raza
Inaltime INIT
INIT AFISEAZA
AFISEAZA ASCUNDE
ASCUNDE ARIE
ARIE
205
Obiecte în Pascal
INTERFACE
Type
TipError=(OK, ZeroNum, TooBigNum);
Var
classErr:TipError;
Constructor TA.Init;
Begin
....................................................
new (Ptr1); {alocare de spatiu dinamic}
new(Ptr2);
....................................................
if (Ptr1=Nil) or (Ptr2=Nil)…….
Then
begin
{cod pentru recuperare}
sau
{cod de terminare + TA.DONE + Fail;}
end;
{continuare constructor}
End;
206
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Begin
HeapError:=@fallocerr;
End.
207
ANEXA 1
UNITATEA DOS
1. Tipuri de date
Tipul Registers
Registers =
Record
CASE INTEGER OF
0: (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:WORD);
1: (AL,AH,BL,BH,CL,CH,DL,DH:BYTE);
End;
Data şi ora
DateTime =
Record
Year : WORD; {Anul}
208
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
209
Unitatea DOS
TextRec =
RECORD
Handle : WORD;
Mode : WORD;
BufSize : WORD;
Private : WORD;
BufPos : WORD;
BufEnd : WORD;
BufPtr : ^TextBuf;
OpenFunc : Pointer;
InOutFunc : Pointer;
FlushFunc : Pointer;
UserData : ARRAY[1...16] OF BYTE;
Name : ARRAY[0...79] OF CHAR;
Buffer : TextBuf;
END;
210
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
2. Constante
3. Variabile
DosError: INTEGER;
Este iniţializată de funcţiile şi procedurile din acest unit, cu următoarele valori
principale:0 - fără eroare; 2 - fişier negăsit; 3 - cale negăsită; 4 - prea multe
fişiere deschise; 5 - acces interzis etc. Variabila DosError are valoarea zero
când execuţia funcţiilor şi a procedurilor definite în unit-ul Dos se termină
normal.
211
Unitatea DOS
4. Proceduri şi funcţii
Prelucrarea fişierelor
Procedure GetFAttr(VAR f; VAR attr:WORD);
Returnează atributele unui fişier.
Procedure SetFAttr(VAR f; VAR attr:WORD);
Poziţionează (setează) atributele unui fişier.
Procedure GetFTime(VAR f; VAR Dl:LONGINT);
Returnează data şi ora ultimei scrieri într-un fişier. Dl este variabila în care se
recepţionează data şi ora, sub formă "împachetată".
Procedure SetFTime(VAR f; Dl:LONGINT);
Înscrie data şi ora în eticheta fişierului. Dl este variabila ce conţine data şi ora
sub formă împachetată, care se înscriu în intrarea fişierului în [sub]director.
Procedure FindFirst(F_extern:PathStr; Attr:WORD; VAR zona:SearchRec);
Caută într-un [sub]director prima intrare a unui fişier care are specificatorul şi
atributul precizate în lista de parametri. F_extern este specificatorul extern al
fişierului căutat. Specificatorul este format din cale, nume fişier şi, eventual,
extensie. Când calea lipseşte se presupune [sub]directorul curent. Numele şi
extensia fişierului pot fi generice (formate cu caracterele * sau ?). Attr
reprezintă valoarea atributelor fişierului care se caută. Zona este o variabilă de
tipul SearchRec, care va conţine informaţii despre fişierul f_extern în cazul
când a fost găsit. Dacă f_extern nu este găsit, variabila zona rămâne
nemodificată. Când f_extern este găsit, variabila DosError va avea valoarea
zero. În caz contrar, DosError are valoare diferită de zero.
Procedure FindNext(VAR zona:SearchRec);
Caută într-un [sub]director următoarea intrare a unui fişier care are
specificatorul şi atributul precizate la apelul anterior al procedurii FindFirst.
Rezultă că procedura FindNext poate fi utilizată numai dacă, anterior, a fost
apelată procedura FindFirst. Zona are aceeaşi semnificaţie ca la procedura
FindFirst. Dacă următoarea intrare este găsită, variabila DosError va avea
valoarea zero. În caz contrar, DosError are valoare diferită de zero.
Function FSearch(Nume:PathStr; Lista:STRING):PathStr;
Caută un fişier într-o listă de [sub]directori. Ea este asemănătoare comenzii
PATH din MSDOS. Nume este variabilă de tip PathStr, care conţine numele
fişierului de căutat. Lista conţine lista directoarelor în care se continuă
căutarea, dacă fişierul nu a fost găsit în directorul curent. Căile specificate în
listă trebuie separate prin caracterul ;. Funcţia returnează specificatorul extern
al fişierului, în cazul în care îl găseşte. Când fişierul este în directorul curent,
specificatorul extern furnizat este format din nume şi, eventual, extensie, iar
când fişierul este în alt director, specificatorul este complet (cale+nume [+
extensie]). Când fişierul nu este găsit, funcţia returnează şirul vid. Funcţia nu
verifică existenţa căilor. Când căile nu există, se returnează şirul vid.
212
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
Function FExpand(Nume:PathStr):PathStr;
Expandează (completează) numele cu calea. Nume este variabilă de tip
PathStr ce conţine numele care urmează să fie expandat cu calea. În procesul
prelucrării fişierelor, funcţia are sens când este utilizată pentru extinderea cu
componente (unitate, cale) furnizate implicit.
Procedure FSplit(specificator:PathStr;VAR unit_dir:DirStr;VAR name:NumeStr;VAR
ext:ExtStr);
Descompune specificatorul extern (unitate, cale, nume, extensie) în trei
componente: unitate+cale, nume, .extensie. Specificator este şirul care se
analizează şi se descompune în componente. Unit_dir, nume şi ext sunt
variabile în care se depun cele trei componente extrase din specificator. Când
unitatea şi calea nu sunt prezente în specificator, se returnează valorile
implicite. Când numele şi extensia nu sunt prezente în specificator, se
returnează şiruri vide.
Procedure SetVerify(v:BOOLEAN);
Poziţionează comutatorul verify din DOS. Comutatorul are două valori: ON
(valoare TRUE) sau OFF (valoare FALSE). Când comutatorul are valoarea
ON, sistemul de operare face verificare după fiecare operaţie de scriere în
fişiere (se verifică dacă datele scrise pot fi citite fără eroare). Poziţia ON a
comutatorului verify măreşte timpul de execuţie a programelor. Când
comutatorul are valoarea OFF, sistemul de operare nu face verificarea scrierii.
Valoarea implicită a comutatorului este OFF. Valoarea comutatorului rămâne
activă până la o nouă setare. V este o variabilă de tip BOOLEAN. Când v are
valoarea TRUE, comutatorul verify primeşte valoarea ON; în caz contrar pri-
meşte valoarea OFF. Procedura are efect similar cu comanda VERIFY din
DOS. Dacă comutatorul verify este ON, rezultatul verificării scrierii este
memorat în variabila DosError.
Ocuparea discului
Function DiskSize(Drive:BYTE):LONGINT;
Returnează capacitatea, în octeţi, a discului montat în unitatea specificată.
Drive specifică unitatea de disc, astfel: 0 - unitatea de disc curentă; 1 - unitatea
de disc A; 2 - unitatea de disc B; 3 - unitatea de disc C etc. Funcţia returnează
valoarea -1 dacă unitatea de disc nu există, dacă nu este montat disc în unitate
sau dacă unitatea este defectă.
Function DiskFree(Drive:BYTE):LONGINT;
Returnează numărul de octeţi liberi de pe un disc montat într-o unitate. Drive
are aceeaşi semnificaţie ca la funcţia DiskSize. Funcţia returnează valoarea -1
dacă unitatea de disc nu există, dacă nu este montat disc în unitate sau dacă
unitatea este defectă.
213
Unitatea DOS
Execuţia programelor
Procedure Exec(specif,parametru:STRING);
Încărcarea în memorie şi lansarea în execuţie a programului aflat în fişierul
specif. Parametrii de pe linia de comandă sunt specificaţi în parametru.
Function DosExitCode:WORD;
Codul de retur al unui proces (program).
Întreruperi
Procedure Intr(ni:BYTE; VAR reg:Registers);
Lansarea întreruperii ni. În variabila reg se pregătesc argumentele.
Procedure MsDos(VAR reg:Registers);
Lansarea întreruperii $21. În variabila reg se pregătesc argumentele.
Procedure GetIntVec(int:BYTE; VAR adrint:POINTER);
Obţinerea, în adrint, a adresei întreruperii cu numărul int.
Procedure SwapVectors;
Salvarea vectorilor de întrerupere.
Data şi timpul
Procedure GetDate(VAR an, luna, ziua_luna, ziua_sapt:WORD);
Obţinerea datei din sistem.
Procedure SetDate(VAR an, luna, ziua:WORD);
Poziţionarea datei în sistem.
Procedure GetTime(VAR ora, minut, sec, sec100:WORD);
Obţinerea timpului din sistem.
Procedure SetTime(VAR ora, minut, sec, sec100:WORD);
Poziţionarea timpului în sistem.
Procedure PackTime(VAR Dc:DateTime; VAR Dl:LONGINT);
"Împachetarea" unui articol de tip DateTime într-o dată de tip LONGINT. Dc
este data şi ora, exprimate pe componente, care urmează să fie "împachetate"
sub formă LONGINT în zona Dl.
Procedure UnpackTime(Dl:LONGINT, VAR Dc:DateTime);
"Despachetarea" unei valori de tip DateTime. Dl este expresie de tip
LONGINT care va fi "despachetată" pe componentele datei şi orei în zona Dc.
Alte funcţii
Function DosVersion:WORD;
Returnează un cuvânt ai cărui octeţi conţin numărul principal al versiunii
curente a sistemului de operare (octetul inferior), respectiv numărul secundar
(octetul superior).
214
ANEXA 2
UNITATEA CRT
1. Constante
Constante pentru culorile de fond şi de text
2. Proceduri şi funcţii
Pregătirea scrierii
Procedure AssignCrt (VAR f:TEXT);
Asignează fişierul f la dispozitivul CRT.
Procedure TextMode (Mode:WORD);
Selectează modul text.
Procedure Window (X1, Y1, X2, Y2:BYTE);
Defineşte fereastra text.
Procedure TextBackground (Color:BYTE);
Defineşte culoarea fondului.
Procedure TextColor (Color:BYTE);
215
Unitatea CRT
216
ANEXA 3
ERORI DE EXECUŢIE
1. Erori DOS
1. Funcţie inexistentă. Generată de un apel al unei funcţii DOS inexistente.
2. Fişier inexistent. Generată de execuţia uneia din procedurile Reset, Append,
Rename sau Erase, dacă identificatorul asignat variabilei de tip fişier nu corespunde
unui fişier existent.
3. Cale inexistentă. Generată de execuţia uneia din procedurile:
• Reset, Append, Rewrite, Rename sau Erase, dacă identificatorul asignat
variabilei de tip fişier este invalid sau include un sub[director] inexistent;
• ChDir, MkDir sau RmDir, dacă sub[directorul] este invalid sau inexistent.
4. Prea multe fişiere deschise. Generată de execuţia uneia din procedurile Reset sau
Append dacă, la un moment dat, sunt deschise simultan mai mult de 12 fişiere ale
utilizatorului. Dacă se doreşte raportarea erorii pentru un număr mai mic de fişiere
deschise simultan, trebuie ca fişierul CONFIG.SYS să nu conţină clauza FILES=xx
sau să specifice numărul de fişiere dorit.
5. Acces interzis la fişier. Generată de execuţia uneia din procedurile:
• Reset sau Append, dacă FileMode permite scrierea, dar identificatorul asig-
nat variabilei fişier specifică un [sub]director/fişier read-only;
• Rewrite, dacă sub[directorul] este plin sau identificatorul asignat variabilei
fişier specifică un [sub]director/fişier existent read-only;
• Rename, dacă identificatorul asignat variabilei fişier specifică un fişier
existent;
• Erase, dacă identificatorul asignat variabilei fişier specifică un sub[direc-
tor]/fişier read-only;
• MkDir, dacă: există un fişier cu aceleaşi nume în sub[directoru] părinte; nu
există spaţiu în sub[directorul] părinte; este specificat în cale un dispozitiv;
• RmDir, dacă: sub[directorul] nu este vid; nu se specifică un sub[director] în
cale; directorul specificat include rădăcina;
• Read/BlockRead pentru un fişier cu tip/fără tip, dacă acesta nu a fost des-
chis pentru citire;
217
Erori de execuţie
2. Erori de intrare/ieşire
Erorile de intrare/ieşire determină întreruperea execuţiei programului, numai
dacă instrucţiunea respectivă a fost compilată cu directiva {$I+} (valoare impicită).
În cazul în care se specifică directiva de compilare {$I-}, execuţia programului conti-
nuă, iar apariţia erorii este depistată cu ajutorul funcţiei IOResult.
218
Programarea calculatoarelor – Tehnica programării în limbajul Pascal
3. Erori critice
150 Disc protejat la scriere
151 Unit necunoscut
152 Dispozitivul nu este pregătit
153 Comandă necunoscută
154 Eroare CRC în dată
155 Cerere pe un dispozitiv greşit
156 Eroare de poziţionare pe disc
157 Tip dispozitiv necunoscut
158 Sector negăsit
159 Imprimantă în aşteptarea hârtiei
160 Incident la scrierea pe dispozitiv
161 Incident la citirea de pe dispozitiv
162 Întrerupere hardware
4. Erori fatale
200 Împărţire la zero. Generată de împărţirea la 0 a unui număr, cu operatorii / ,
MOD sau DIV.
201 Nonapartenenţă la un interval. Generată de instrucţiunile compilate cu
directiva {$R+}, în următoarele condiţii:
• expresia de indice pentru referirea unui element de masiv este în afara
intervalului;
• atribuirea unei valori în afara intervalului stabilit pentru variabila
respectivă;
• atribuirea unei valori în afara intervalului stabilit pentru un parametru de
procedură/funcţie.
202 Depăşire stivă. Generată la apelul unei proceduri/funcţii, compilate cu directiva
{$S+}, când nu este spaţiu suficient în stivă pentru memorarea variabilelor locale.
Stiva se poate mări cu directiva de compilare {$M}. Eroarea apare şi în cazul unui
apel recursiv infinit.
203 Depăşire heap. Generată de execuţia uneia din procedurile New sau GetMem,
când nu este suficient spaţiu în heap, pentru alocarea unui bloc sau a unei zone de
mărime specificată.
204 Operaţie cu pointer invalid. Generată de execuţia uneia din procedurile
Dispose sau FreeMem dacă: pointerul are valoarea nil sau indică o locaţie în afara
zonei heap; lista blocurilor libere nu poate fi extinsă, deoarece este plină; HeapPtr
are o valoare prea apropiată de limita inferioară a listei libere.
205 Depăşire virgulă mobilă. Generată în urma unei operaţii al cărei rezultat este
un număr prea mare pentru a fi reprezentat într-un tip real de dată Pascal.
206 Depăşire inferioară virgulă mobilă. Generată în urma unei operaţii al cărei
rezultat este un număr prea mic pentru a fi reprezentat într-un tip real de dată Pascal.
219
Erori de execuţie
220
BIBLIOGRAFIE
221