Uvod
Uobičajeni način zapisivanja matematičkih izraza (koji podrazumeva da se znak operacije zapisuje između dva operanda), naziva se infiksna notacija i ima sledeće dobro poznate odlike: znaci operacija povezuju operande (promenljive i brojčane vrednosti), operacije množenja i deljenja imaju veći prioritet od operacija sabiranja i oduzimanja, zagrade se pojavljuju po potrebi i imaju najveći prioritet.
Shodno poznatim pravilima, do vrednosti izraza dolazi se na nedvosmislen način, kao na primer u izrazu: 5 * (2 + 4)
, u kome sabiranje prethodi množenju - i rezultat je 30
.
Međutim, iako je, za ljude, postupak računanja preko infiksne notacije razumljiv, jednostavan za usvajanje i (reklo bi se) prijemčiv i neproblematičan, za korišćenje u računarskoj tehnici - infiksna notacija ni iz daleka nije najbolji izbor.
Možda ne deluje intuitivno na prvi pogled, ali, izrazi nalik na ab+
i ab+cd-/
, koji su zapisani preko tzv. postfiksne notacije, na računarima se rešavaju mnogo brže od infiksnih izraza kao što su a+b
i (a+b)/(c-d)
.
U nastavku ćemo se pozabaviti time kako se infiksni izrazi pretvaraju u postfiksne, kao i time kako se računa vrednost postfiksnog izraza, a za sam početak, prodiskutovaćemo detaljnije o tome zašto infiksna notacija nije optimalan izbor za računarske algoritme koji se tiču računanja vrednosti izraza.
Nedostaci infiksne notacije
( .... u kontekstu primene u računarskim algoritmima )
Ideja da se uobičajena infiksna notacija koristi i u računarskim algoritmima, verovatno deluje "prirodno", ali, u implementaciji takve ideje, postoje problemi koji se (najprostije rečeno), svode na to da računari nisu sposobni da odjednom sagledaju "širu sliku". *
Čak ni u slučaju jednostavnog infiksnog izraza kao što je: a * (b + c)
, računar ne može sa sigurnošću izvršavati operacije u onom trenutku kada se pojave, već mora bar jednom proći kroz ceo izraz (u najboljem slučaju) - i tek onda se vratiti na izvršavanje operacija.
Zapravo, izraz ne mora imati čak ni dve operacije: izraz kao što je (na primer) a + b
je (skoro) najjednostavniji mogući, ali - ni u ovom slučaju računar ne sme odmah da obavi sabiranje (sve dok ne uzme u obzir sve operande i operatore).
Moglo bi se reći da - iz današnje perspektive - ne deluje "strašno" da program (zapravo) prođe kroz izraz više puta, uzme u obzir sve operande, operatore, zagrade i .... "nekako" .... odredi prioritet izvršavanja operacija, jer u današnje vreme, čak i najslabiji procesor će sve navedeno izvršiti u malom deliću sekunde (ili, jednostavnije, ne deluje da je postupak računanja preko infiksne notacije "posebno problematičan"), međutim:
- kao programeri, u obavezi smo da uvek tražimo što lepša i što efikasnija rešenja (pogotovo kada su u pitanju elementarni problemi) - i time, generacijama programera koje dolaze posle nas, ostavimo što manje 'zbrke'
- poslednje što smo naveli, posebno je važilo na početku razvoja računarske industrije kada računari, ne samo da nisu bili veoma brzi, već su (naprotiv), bili prilično 'tromi' *
U praktičnom smislu, nedostatak brzine prvobitnih računara (čak i po pitanju računanja vrednosti matematičkih izraza), bio je takav da je neko vreme dovodio u pitanje početak razvoja računarske industrije ** ("ako računari ne mogu efikasno da rešavaju jednostavne matematičke izraze, kako se onda mogu koristiti za komplikovanije zadatke?!"), ali, problem je rešio holandski naučnik Edzger Dajkstra i ostavio pokoljenjima lep, jednostavan i efikasan postupak koji se zasniva na isto tako lepom, jednostavnom i efikasnom (ali, nimalo uobičajenom), načinu beleženja matematičkih izraza.
Postfiksna notacija
Postfiksna notacija * je sistem za zapisivanje matematičkih izraza, sa sledećim glavnim odlikama:
- operatori se pojavljuju posle operanada
- zagrade se ne pojavljuju u izrazima
Sistem zapisivanja izraza u postfiksnom obliku, razradio je australijski naučnik Čarls Hamblin, sredinom pedesetih godina 20. veka, po ugledu na (idejno sličnu) prefiksnu notaciju, koju je izumeo poljski matematičar Jan Lukašijevič i objavio 1924. godine.
Na prvi pogled, zapisivanje izraza bez zagrada može delovati "čudno", ali, uz iole ozbiljnije udubljivanje, prednosti postaju veoma očigledne - pogotovo kad je u pitanju upotreba u računarskim sistemima. **
Oblik izraza u postfiksnoj notaciji
Najočiglednija odlika postfiksne notacije je (kao što smo prethodno naveli), pojava znaka operacije posle operanada, pa tako na primer:
- izraz
a + b
postajeab+
- izraz
a + (b * c)
postajeabc*+
Da bismo razumeli pravi smisao ovakvog načina zapisivanja (to jest, da bismo razumeli zašto postfiksni izrazi predstavljaju praktičniji način zapisa matematičkih izraza na računarima, u odnosu na infiksnu notaciju), potrebno je sagledati kako se računa vrednost postfiksnih izraza, i potrebno je razmotriti kako postfiksni izrazi (uopšte) nastaju.
Postupak računanja vrednosti postfiksnog izraza pokazaćemo odmah (u nastavku), u daljem toku članka pokazaćemo na koji način ljudi mogu pretvarati infiksne izraze u postfiksne (sagledavanjem i rasuđivanjem), dok ćemo nešto komplikovanijem algoritamskom postupku za pretvaranje infiksnih izraza u postfiksne, uskoro posvetiti nekoliko članaka.
Pri računanju vrednosti izraza, smatraćemo da promenljive imaju sledeće vrednosti:
- a = 4
- b = 3
- c = 2
.... iz čega sledi da vrednost izraza a * (b + c)
, odnosno, izraza 4 * (3 + 2)
, iznosi 20.
Kada se, u postfiksnoj notaciji, promenljive zamene navedenim (konkretnim) vrednostima, izraz postaje:4 3 2 + *
.
Računanje vrednosti
Postfiksni izrazi rešavaju se na sledeći način:
- posmatrajući elemente sleva nadesno, traži se prvi (ili, u opštem smislu - sledeći ), znak operacije
- kada se pronađe znak operacije, nad dve vrednosti levo od znaka, obavlja se operacija koja odgovara znaku
- umesto dva operanda i znaka (iz prethodnog koraka), u izrazu ostaje samo rezultat operacije
- postupak se ponavlja sve dok u izrazu (posle poslednje izvršene operacije), ne ostane samo jedan operand
- preostali operand predstavlja vrednost početnog izraza
U našem slučaju (u izrazu: 4 3 2 + *
), prvo se u obzir uzima znak operacije sabiranja, a u obzir se uzimaju i dve brojčane vrednosti sa leve strane (ili praktično, prvo se rešava izraz 3 2 +
):
- obavlja se operacija sabiranja
- rezultat sabiranja (5), ostaje u izrazu umesto brojeva 3 i 2
- znak koji predstavlja operaciju sabiranja, uklanja se iz izraza
Izraz postaje: 4 5 *
(na sličan način kako je izraz 4 * (3 + 2)
mogao biti sveden na izraz 4 * 5
) i preostaje samo, da se postupak ponovi sa operacijom množenja - pri čemu se dobija rezultat 20.
Poenta je sledeća: ceo izraz (kao što vidimo), rešava se u jednom prolasku, to jest, svaka operacija obavlja se u onom trenutku kada se naiđe na znak operacije:
- nema dvoumljenja (za ljude)
- relativno lako se može osmisliti i računarski algoritam koji funkcioniše po sličnom principu (bez grananja/odlučivanja)
Ukratko o prefiksnoj notaciji
Prefiksna notacija (nije teško pretpostaviti), podrazumeva sličan pristup kao i postfiksna notacija, s tom razlikom što se operator beleži pre operanada:
- izraz
a + b
postaje+ab
- izraz
a + (b * c)
postaje+a*bc
Procena vrednosti obavlja se 'u obrnutom smeru' (u odnosu na idejno sličan postupak koji smo videli u prethodnom poglavlju): u drugom izrazu, prvo se obavlja množenje, pa onda sabiranje.
"Neobrnuta" poljska notacija takođe se koristi u računarskim algoritmima (bila je posebno popularna na prvim elektronskim kalkulatorima, pre više decenija), ali, za sada ćemo se zadržati na proučavanju (i korišćenju) postfiksne notacije, budući da takav zapis (ipak) prirodnije odgovara većini algoritama.
Uglavnom, u oba slučaja, oslobodili smo se zagrada (posle čega se vrednost izraza može izračunati u jednom prolasku) i samo još preostaje pitanje: kako od infiksnog izraza napraviti postfiksni.
Prebacivanje izraza iz infiksnog zapisa u postfiksni
Pre upoznavanja sa 'zvaničnim' postupkom za pretvaranje infiksnih izraza u postfiksne (što ostavljamo za drugu priliku), potrebno je da se upoznamo sa idejama koje stoje iza postupka - a to ćemo najlakše izvesti ukoliko izraze predstavimo preko grafičkih simbola (ili jednostavnije, preko "slika").
U izrazu prvo treba uočiti operaciju koja, u širem kontekstu, ima najniži prioritet (to jest, operaciju koja se izvršava 'na kraju') i potom treba postaviti znak operacije u koren "stabla" izraza (u konkretnom izrazu koji koristimo kao primer: a * (b + c)
, u pitanju je operacija množenja).
Sa leve strane biće promenljiva a
(koja u množenju učestvuje samostalno), dok će sa desne strane biti "podstablo" koje samo po sebi predstavlja operaciju sabiranja promenljivih b
i c
(operacija koja se, očigledno, obavlja pre množenja zbira b + c
sa promenljivom a
).

Stablo se obilazi na sledeći način: počevši od korena, na svakom čvoru/raskršću koji predstavlja operaciju, prvo se "skreće" levo, a tek kada se dođe do 'krajnjeg levog čvora' (u bilo kom podstablu), sledi povratak jedan korak unazad - na čvor koji predstavlja operaciju - i potom se skreće desno. Ali - posle svakog desnog skretanja (kada se nađemo u novom podstablu) - opet se prvo skreće levo (i opet se "držimo leve strane").
Kada se u kretanju kroz stablo pronađe operand (promenljiva), odmah se prebacuje u postfiksni izraz, međutim, operatori (znaci operacija), mogu se prebacivati tek pošto je celo podstablo koje je vezano za operaciju (tj, podstablo koje u korenu ima znak operacije), već prebačeno u postfiksni izraz.
Krenimo redom i pogledajmo kako postupak izgleda na primeru izraza koji smo do sada koristili: a * (b + c)
.
Prvo se u obzir uzima poslednja operacija koja se izvršava (znak množenja na vrhu stabla) ....

.... ali, pošto nijedan od znakova iz podstabala koja izviru iz korenog čvora, nije upisan, znak množenja za sada se mora preskočiti.

Prelazimo na levu stranu ....

Čvor predstavlja promenljivu, i stoga će odmah biti prebačen u izraz sa postfiksnom notacijom.

Ponovo se vraćamo na prvi čvor ....

.... ali, budući da ni sada nisu rešena oba podstabla korenog čvora (iako je leva strana rešena), čvor *
se ni ovoga puta ne može prebaciti u izraz sa postfiksnom notacijom.

Pošto u prethodnim koracima nismo završili u potpunosti sa prvim čvorom, ali, jesmo završili sa levim podstablom (čvor a
), prelazimo na desno podstablo.
U desnom podstablu, prvo se ispituje čvor koji odgovara operaciji sabiranja ....

.... ali, budući da nijedno podstablo ovog čvora nije rešeno, za sada (u smislu ispisa), preskačemo operator sabiranja.

Prelazimo levo, pronalazimo promenljivu ....

.... i operand b
se odmah prebacuje u izraz sa postfiksnom notacijom.

Vraćamo se (ponovo) na operator sabiranja i ispitujemo da li je spreman za prebacivanje u postfiksni izraz ....

.... međutim, budući da desno podstablo operatora +
nije rešeno, operator +
i dalje se ne upisuje u postfiksni izraz.

Prelazimo u desno podstablo operacije sabiranja, nailazimo na promenljivu .....

.... i operand c
se odmah može prebaciti u izraz sa postfiksnom notacijom.

Ponovo se vraćamo na čvor +
....

.... i sada se čvor +
može prebaciti u izraz sa postfiksnom notacijom, budući da je rešeno celo podstablo koje se odnosi na operaciju sabiranja.

Na kraju, vraćamo se na koreni čvor ....

.... i budući da je celo stablo, osim korenog čvora, već prebačeno u izraz sa postfiksnom notacijom, može se upisati i sam koreni čvor.

Cela operacija je završena: sve promenljive i svi znakovi operacija, prebačeni su u postfiksnu notaciju.

Ukoliko je potrebno, pogledajte ponovo ceo primer.

Za vežbu
Na kraju, pokušajte sami da pretvorite bar nekoliko * infiksnih izraza u izraze sa postfiksnom notacijom - na papiru.
Za proveru rezultata možete koristiti mini-aplikaciju ispod (dozvoljeni znaci: mala slova, znaci operacija i zagrade).
--------