Sunteți pe pagina 1din 11

2.

Arbori asociai expresiilor aritmetice


Rolul oricrui compilator este de a transforma programele scrise ntr-un limbaj de programare oarecare ntr-un cod echivalent, scris n limbaj de asamblare sau n limbaj main. n acest capitol ne punem problema translatrii expresiilor aritmetice dintr-un limbaj de programare cum ar fi de exemplu limbajul Pascal n limbaj de asamblare. Pentru aceasta ne vom propune un model simplu de main, care lucreaz cu regitri notai R1, R2,... i execut urmtoarele dou operaii: PUNE Rx variabil/constant: ncarc variabila sau constanta n registrul Rx; OPz Rx Ry: efectueaz operaia OPz asupra coninuturilor regitrilor de memorie Rx respectiv Ry, rezultatul fiind depus n registrul Rx. De exemplu, pentru expresia aritmetic (a+b)*(c+2) se poate asocia codul: PUNE R1 a PUNE R2 b OP+ R1 R2 PUNE R2 c PUNE R3 2 OP+ R2 R3 OP* R1 R2 Primul pas n rezolvarea acestei probleme este de asocia expresiei aritmetice un arbore binar. Definiie Numim expresie aritmetic o construcie definit prin urmtoarele diagrame de sintax:

56

Observaie Am ales pentru simplitate expresii aritmetice pentru care operanzii sunt formai dintr-un singur caracter, iar operatorii pot fi doar + i *. Aceste restricii nu sunt semnificative. Oricrei expresii aritmetice i se poate asocia un arbore binar strict, construit dup urmtoarele reguli:* -dac expresia este format dintr-un singur operand, arborele binar asociat este constituit dintr-un singur nod, rdcina, care conine drept informaie operandul respectiv; -dac expresia este de forma E = E 1 op E2, unde op este un operator, iar E1 i E2 sunt expresii aritmetice, arborele binar corespunztor conine n rdcin operatorul, subarborele stng fiind arborele binar asociat expresiei E1, iar subarborele drept arborele binar asociat expresiei E2. De exemplu, arborele binar asociat expresiei E = (a+b)*(a*c+b*d) este:

Fig. 1.

Programul Expresie-Aritmetica de la sfritul capitolului construiete arborele binar asociat unei expresii corecte din punct de vedere sintactic, afieaz forma polonez a expresiei i genereaz codul optimal pentru expresia dat. 57

Observaii 1. ntr-un arbore binar asociat unei expresii aritmetice nodurile terminale conin operanzi, iar nodurile interioare conin operatori. 2. Operatorii utilizai n cadrul expresiei fiind binari, arborele binar asociat expresiei aritmetice este strict. Parcurgnd n preordine arborele binar asociat expresiei aritmetice, obinem notaia polonez a expresiei (forma prefixat). De exemplu, pentru expresia aritmetic E=(a+b)*(a*c+b*d), notaia polonez este *+ab+*ac*bd. Aceast notaie a fost introdus de matematicianul polonez J. Lucasiewicz i permite scrierea fr paranteze a unei expresii aritmetice. Pentru a genera codul asociat unei expresii aritmetice vom presupune pentru nceput c operatorii + i * nu sunt comutativi i nici asociativi. Vom descrie o procedur de generare a codului corespunztor unei expresii aritmetice reprezentate ca un arbore binar, utiliznd un numr minim de regitri de memorie. Lem Fie A arborele binar asociat unei expresii aritmetice i x un nod din A. Numrul minim de regitri necesari pentru a evalua expresia corespunztoare subarborelui cu rdcina x este:

Fig. 2.

R |1, MR( x) = S max(MR( x^ .st),MR( x^ .dr )), | MR(x^ .st)+ 1, T


Observaii
58

dac x este un nod terminal; dac MR(x^ .st)MR(x^ .dr); dac MR(x^ .st)= MR(x^ .dr)

1. Demonstraia se poate face prin inducie dup nlimea subarborelui cu rdcina n x i o propunem ca exerciiu. 2. Pentru orice nod x din arbore, se poate calcula MR(x) parcurgnd arborele n postordine. procedure GenCod (rad: Arbore); //genereaz codul asociat expresiei aritmetice reprezentate prin //arborele cu rdcina rad, fr a ine cont de proprietile de //comutativitate i asociativitate ale operatorilor //UR este o variabil global care indic numrul ultimului registru //ocupat begin if rad^.st = nil then //expresie format dintr-un singur operand begin inc(UR); writeln(PUNE R, UR, , rad^.inf); end else if MR(rad^.dr) > MR(rad^.st) then begin //genereaz mai nti codul asociat subarborelui drept GenCod(rad^.dr); GenCod(rad^.st); writeln(OP, rad^.inf, R, UR-1, R, UR); dec(UR); end else begin //genereaz mai nti codul asociat subarborelui stng GenCod(rad^.st); GenCod(rad^.dr); writeln(OP, rad^.inf, R, UR-1, R, UR); dec(UR); end
59

end; Dac sunt luate n considerare i proprietile de comutativitate i asociativitate ale operatorilor, atunci aceeai expresie poate fi reprezentat echivalent prin arbori binari diferii. De exemplu,

Fig. 3. n acest caz, numrul de regitri de memorie utilizai, ct i timpul de evaluare a expresiei, difer n funcie de forma arborelui asociat expresiei.

Exerciii: 1. Construii un arbore binar pentru fiecare din expresiile urmtoare i determinai notaia polonez. a) a*b*c+2*d*(a+1+2*b*c) b) 2*a*c*(a*b+c+3*d+a*b*c*d) c) Evaluai complexitatea procedurii de generare a codului corespunztor unei expresii aritmetice. d) Scriei un program care s citeasc un ir de caractere i s verifice dac reprezint o expresie aritmetic valid (n sensul definiiei de mai sus). n caz afirmativ, s se aduc expresia n form canonic, aplicnd ori de cte ori este nevoie distributivitatea nmulirii fa de adunare (a*(b+c)=a*b+a*c; (b+c)*a=b*a+b*c) i, eventual, idempotena (a+a=a; a*a=a). e) Dat o expresie aritmetic n notaie polonez, scriei un program care s evalueze expresia,
60

pentru valori date ale operanzilor. f) Date dou expresii aritmetice sintactic valide, scriei un program care s verifice dac cele dou expresii sunt echivalente. Indicaie: Se vor aduce cele dou expresii n form canonic, innd cont de proprietile de comutativitate i asociativitate ale operatorilor i aplicnd ori de cte ori este necesar regulile de distributivitate i idempoten. 6. (Propus de conf. dr. Florian Boian la Olimpiada Internaional de Informatic Cluj, 1994) Dat fiind o expresie aritmetic, notm p timpul necesar pentru efectuarea unei adunri i q timpul necesar pentru efectuarea unei nmuliri, timpul necesar pentru evaluarea expresiei E1 op E2 fiind egal cu timpul necesar pentru efectuarea operaiei op plus maximul dintre timpii necesari evalurii subexpresiilor E1 i E2. Timpul de evaluare a unui operand este considerat nul. Scriei un program care citete date dintr-un fiier ce conine valorile lui p i q i expresii, fiecare expresie pe o linie separat. Pentru fiecare expresie se cere: -S se determine timpul necesar evalurii ei; -S se determine o expresie echivalent cu timp de evaluare minim i valoarea acestui timp. Transformrile permise sunt: a*b=b*a; a+b=b+a (a*b)*c=a*(b*c); (a+b)+c=a+(b+c). Indicaie Transformai arborele binar asociat expresiei aritmetice ntr-un arbore binar echilibrat dup nlime, aplicnd rotaii simple sau duble, echivalente cu aplicarea proprietilor de comutativitate i asociativitate. Anex
program Expresie_Aritmetica;
const LgMax = 30; type Indice = 1..LgMax; Arbore = ^NodArbore; NodArbore = record inf: char; st, dr: Arbore; end; var e: string[LgMax]; rad: Arbore; UR: byte; i, lg: Indice; fout: text;

61

procedure Citire; begin write('Introduceti expresia '); readln(e); lg := length(e); end; procedure preordine(r: Arbore); {parcurge arborele binar asociat expresiei aritmetice in preordine} begin if r <> nil then begin write(fout, r^.inf); preordine(r^.st); preordine(r^.dr); end; end; function CreareArbore(var i: Indice): Arbore; forward; function Factor(var i: Indice): Arbore; {construieste arborele binar asociat unui factor} var p: Arbore; begin if e[i] = '(' then begin inc(i); Factor := CreareArbore(i); inc(i); end else begin new(p); p^.inf := e[i]; p^.st := nil; p^.dr := nil; inc(i); Factor := p; end; end; function Termen(var i: Indice): Arbore; {construieste arborele binar asociat unui termen} var p, r1: Arbore; begin r1 := Factor(i); if (i > lg) or (e[i] <> '*') then Termen := r1 else begin new(p); p^.inf := e[i]; inc(i); p^.st := r1; p^.dr := Termen(i); Termen := p; end end;

62

function CreareArbore(var i: Indice): Arbore; {construieste arborele binar asociat unei expresii aritmetice} var p, r1: Arbore; begin if i > lg then CreareArbore := nil else begin r1 := Termen(i); if (i > lg) or (e[i] = ')') then CreareArbore := r1 else begin new(p); p^.inf := e[i]; p^.st := r1; inc(i); p^.dr := CreareArbore(i); CreareArbore := p; end; end; end; function MR(rad: Arbore): byte; var m1, m2: byte; begin if rad^.st = nil then {operand} MR := 1 else begin m1 := MR(rad^.st); m2 := MR(rad^.dr); if m1 = m2 then MR := m1+1 else if m1 > m2 then MR := m1 else MR := m2; end end; procedure GenCod(rad: Arbore); {genereaza codul asociat expresiei aritmetice reprezentate prin arborele cu radacina rad} {UR este o variabila globala care indica numarul ultimului registru ocupat} begin if rad^.st = nil then begin {expresie formata dintr-un singur operand} inc(UR); writeln(fout, 'PUNE R', UR, ' ', rad^.inf); end else if MR(rad^.dr) > MR(rad^.st) then begin {genereaza mai intai codul asociat subarborelui drept } GenCod(rad^.dr); GenCod(rad^.st);

63

writeln(fout,'OP',rad^.inf,' R',UR-1,' R',UR); dec(UR); end else begin {genereaza mai intai codul asociat subarborelui stang} GenCod(rad^.st); GenCod(rad^.dr); writeln(fout,'OP',rad^.inf,' R',UR-1,' R',UR); dec(UR); end end; begin {program principal} Citire; i := 1; assign(fout,'expr.out'); rewrite(fout); rad := CreareArbore(i); preordine(rad); writeln(fout); UR := 0; GenCod(rad); writeln(fout, 'Pentru evaluarea expresiei au fost necesari ', MR(rad),' registri'); close(fout); end.

De exemplu, pentru expresia aritmetic E=(a+b)*(1+c)+2*a*b, coninutul fiierului de ieire va fi: +*+ab+1c*2*ab PUNE R1 a PUNE R2 b OP+ R1 R2 PUNE R2 1 PUNE R3 c OP+ R2 R3 OP* R1 R2 PUNE R2 a PUNE R3 b OP* R2 R3 PUNE R3 2 OP* R2 R3 OP+ R1 R2 Pentru evaluarea expresiei au fost necesari 3 registri.

64

65