nav_dugme codeBlog codeBlog
  • početna Početna stranica
  • Sačuvani članci Sačuvani članci
  • Članci
     (spisak)
  • Kontakt
Povratak na vrh stranice

Info & povezani članci Info o članku - dugme

Info

trejler_sat Datum objave: 17.11.2020.

trejler_dokument Jezici: C#

trejler_teg_narandzasti Težina: 8/10

postfiksna notacija
shunting yard
edsger dijkstra
dijkstra
C#
algoritam
o(n)
strukture podataka
stablo izraza
stack
stek
queue
red za čekanje
lista
optimizacija
teorija
zanimljivosti

Tema: Shunting yard

Postfiksna notacija - kako računari računaju?Implementacija - 1. deo - Prevođenje izraza iz infiksnog u postfiksni zapisBinarna stabla i algebarski izrazi (stablo izraza)
Generator stabla izraza (web aplikacija)

Povezani članci

Strukture podatakaTutorijal - Prepoznavanje algebarskih izraza u programskim kodovimaTutorijal: Implementacija jednostruko ulančane liste u programskom jeziku C++Binarno stablo pretrageKlase složenosti algoritama (O notacija)Metode za optimizaciju algoritamaKako napraviti syntax highlighterIzbor prvog programskog jezikaASCII, Unicode i UTF - Predstavljanje znakova na računarimaGNU/Linux - 1. deo - Uvod
Svi članci
Don't write better error messages, write code that doesn't need them.
Jason C. McDonald

Shunting Yard - Implementacija - 2. deo - Računanje vrednosti postfiksnog izraza

Facebook LinkedIn Twitter Viber WhatsApp E-mail
zoom_plus zoom_minus bookmark
početna > Članci > Teorija

Uvod

U drugom članku o implementaciji algoritma Shunting Yard bavićemo se delom algoritma koji se tiče računanja vrednosti postfiksnog izraza (koji je prethodno dobijen pretvaranjem infiksnog izraza, preko postupka koji smo opisali u prethodnom nastavku - ili je jednostavno u pitanju izraz koji je neposredno zapisan u postfiksnom obliku).

I ovoga puta, u pitanju je algoritam linearne složenosti, koji se može implementirati na jednostavan način ....

Algoritam

Pošto smo se sa postupkom za računanje vrednosti postfiksnog izraza upoznali još u uvodnom članku, ovoga puta se nećemo previše baviti teorijom i odmah ćemo preći na praktični deo, pri čemu ćemo za primer koristiti sledeći izraz ....

		
a * (b - c) - d
		
	
Slika 1. - Infiksna notacija izraza koji će biti korišćen kao primer.

.... koji, posle prebacivanja u postfiksnu notaciju, ima sledeći oblik:

		
abc-*d-
		
	
Slika 2. - Postfiksna notacija izraza koji će biti korišćen kao primer.

Nekoliko smernica za početak:

  • za privremeni smeštaj tokena biće korišćen stek
  • tokenima koji predstavljaju operande (tj. promenljive), biće pripisane konkretne vrednosti

Pre nego što tokenima pripišemo brojčane vrednosti, sagledaćemo ukratko šematski prikaz struktura koje učestvuju u računanju vrednosti (posle čega ćemo predstaviti algoritam za računanje):

Shunting Yard računanje vrednosti 03
Slika 3. - Šema algoritma za računanje vrednosti postfiksnog izraza - Početna situacija

Kada smo prvi put u prethodnom članku prikazali sličnu "šemu", osvrnuli smo se na "moguće putanje", međutim, ovoga puta postoji jedna putanja: tokeni iz reda za čekanje (koji je popunjen u prvoj etapi, i koji praktično predstavlja postfiksni izraz), mogu prelaziti (samo) na stek, a što se tiče ostalih pomoćnih struktura koje vidimo na slici - sledi objašnjenje (u okviru opisa postupka za računanje).

Postupak za računanje vrednosti postfiksnog izraza (poznat od ranije i ovoga puta uobličen u algoritam), sastoji se iz sledećih koraka:

  • tokeni iz reda za čekanje, * obrađuju se jedan za drugim
  • ukoliko se iz reda preuzima operand (tj. promenljiva), token se smešta direktno na vrh steka
  • ukoliko se iz reda preuzima operator (koji se pri tom uklanja iz reda), sledi računanje vrednosti izraza koji odgovara operatoru (i postavljanje rezultata na stek):
    • sa vrha steka se preuzimaju (i praktično uklanjaju), dva operanda
    • operandi se privremeno smeštaju na pomoćne lokacije, pri čemu se vodi računa o redosledu **
    • obavlja se operacija (uz pamćenje rezultata operacije)
    • na stek se vraća rezultat operacije (praktično - u vidu novog operanda)
  • posle obrade svih tokena iz reda za čekanje, token koji stoji na vrhu steka - predstavlja krajnji rezultat

* Kao što smo na početku nagovestili, tokeni koji predstavljaju postfiksni izraz mogu se nalaziti u redu za čekanje (koji je popunjen u prvoj etapi tumačenja infiksnog izraza), ili mogu biti smešteni u listu koja je popunjena neposredno.

(Primer koji ćemo koristiti u članku, odnosi se na prvu navedenu situaciju.)

** Kod operacija sabiranja i množenja, redosled operanada nije bitan (npr. a+b = b+a), ali, u slučaju operacija oduzimanja i deljenja, redosled je i te kako bitan (a/b ≠ b/a).

Pogledajmo, preko konkretnog primera, kako algoritam funkcioniše u praksi.

Praktičan primer

Kao što smo već nagovestili, jasno je da se vrednost izraza ne može izračunati ukoliko u izrazu ostanu apstraktne slovne oznake za operande (tj. identifikatori promenljivih), i stoga je prvo potrebno da se tokeni sa identifikatorima zamene tokenima koji predstavljaju konkretne brojčane vrednosti.

Tokeni a, b, c i d biće povezani sa sledećim vrednostima:

  • a = 2
  • b = 5
  • c = 1
  • d = 3
Shunting Yard računanje vrednosti 04
Slika 4. - Šema algoritma za računanje vrednosti postfiksnog izraza - Promenljive su zamenjene odgovarajućim brojčanim vrednostima.

.... nakon čega se može pristupiti računanju vrednosti izraza.

Prvi token (koji predstavlja operand) ....

Shunting Yard računanje vrednosti 05
Slika 5. - Računanje vrednosti postfiksnog izraza - korak 1. - Token za obradu je operand (broj 2).

.... smešta se na stek:

Shunting Yard računanje vrednosti 06
Slika 6. - Računanje vrednosti postfiksnog izraza - korak 2. - Token se smešta direktno na stek.

Drugi token (5), takođe operand ....

Shunting Yard računanje vrednosti 07
Slika 7. - Računanje vrednosti postfiksnog izraza - korak 3. - Token za obradu je operand (broj 5).

.... takođe se smešta direktno na stek:

Shunting Yard računanje vrednosti 08
Slika 8. - Računanje vrednosti postfiksnog izraza - korak 4. - Token se smešta direktno na stek.

Isto važi i za treći token ....

Shunting Yard računanje vrednosti 09
Slika 9. - Računanje vrednosti postfiksnog izraza - korak 5. - Token za obradu je operand (broj 1).

.... (operand 1 se smešta na stek):

Shunting Yard računanje vrednosti 10
Slika 10. - Računanje vrednosti postfiksnog izraza - korak 6. - Token se smešta direktno na stek.

Četvrti token (-) ....

Shunting Yard računanje vrednosti 11
Slika 11. - Računanje vrednosti postfiksnog izraza - korak 7. - Token za obradu je operator (-).

.... predstavlja znak operacije, što podrazumeva drugačiji vid obrade: sledi računanje vrednosti 'podizraza' (koji obuhvata jedan operator i dva operanda).

Sa steka se uklanjaju dva gornja elementa ....

Shunting Yard računanje vrednosti 12
Slika 12. - Računanje vrednosti postfiksnog izraza - korak 8. - Sa vrha steka biće skinuta dva operanda (1 i 5), koji odgovaraju operatoru koji je trenutno u obradi.

.... i prebacuju se na pomoćne lokacije za obradu:

Shunting Yard računanje vrednosti 13
Slika 13. - Računanje vrednosti postfiksnog izraza - korak 9. - Dva operanda sa vrha steka, smeštaju se na privremene lokacije, i nad njima se obavlja operacija (oduzimanje), koju definiše token za obradu.

Da se podsetimo usput: baš kao što je prikazano na gornjoj slici (i kao što smo ranije napomenuli), strogo se mora voditi računa o tome kako operandi (koji se uklanjaju sa steka) treba da budu raspoređeni u pomoćne memorijske lokacije - da ne bi bio promenjen smisao i rezultat operacija oduzimanja i deljenja!

Pošto su tokeni prebačeni na pomoćne lokacije, računa se vrednost izraza ....

Shunting Yard računanje vrednosti 14
Slika 14. - Računanje vrednosti postfiksnog izraza - korak 10. - Rezultat prethodne operacije (4), smešta se u zasebnu promenljivu (koja će u sledećem koraku, u vidu tokena, biti prebačena na stek).

.... posle čega se rezultat vraća nazad na stek:

Shunting Yard računanje vrednosti 15
Slika 15. - Računanje vrednosti postfiksnog izraza - korak 11. - Rezultat prethodne operacije (4), prebačen je na stek.

Pošto je obrada izraza 5 - 1 završena, nastavlja se obrada tokena iz reda za čekanje - i ponovo nailazimo na znak operacije:

Shunting Yard računanje vrednosti 16
Slika 16. - Računanje vrednosti postfiksnog izraza - korak 12. - Token za obradu je operator (*).

I ovoga puta, potrebno je operator upariti * sa dva gornja elementa sa steka:

Shunting Yard računanje vrednosti 17
Slika 17. - Računanje vrednosti postfiksnog izraza - korak 13. - Sa vrha steka biće skinuta dva operanda (4 i 2), koji odgovaraju operatoru koji je trenutno u obradi.

* U implementaciji, "uparivanje" podrazumeva da program prepoznaje da na steku postoje (bar) dva operanda koji (praktično) odgovaraju operatoru koji se preuzima iz reda za čekanje (a kao što smo već naveli, veoma je bitno da se uvaži i redosled operatora pri izvršavanju operacije).

Vraćamo se na računanje vrednosti podizraza ....

Tokeni se smeštaju na pomoćne memorijske lokacije ....

Shunting Yard računanje vrednosti 18
Slika 18. - Računanje vrednosti postfiksnog izraza - korak 14. - Dva operanda sa vrha steka, smeštaju se na privremene lokacije, i nad njima se obavlja operacija (množenje), koju definiše token za obradu.

.... obavlja se operacija množenja ....

Shunting Yard računanje vrednosti 19
Slika 19. - Računanje vrednosti postfiksnog izraza - korak 15. - Rezultat prethodne operacije (8), smešta se u zasebnu promenljivu (koja će u sledećem koraku, u vidu tokena, biti prebačena na stek).

.... a rezultat se vraća nazad na stek:

Shunting Yard računanje vrednosti 20
Slika 20. - Računanje vrednosti postfiksnog izraza - korak 16. - Rezultat prethodne operacije (8), prebačen je na stek.

Šesti token (operand 3) ....

Shunting Yard računanje vrednosti 21
Slika 21. - Računanje vrednosti postfiksnog izraza - korak 17. - Token za obradu je operand (broj 3).

.... smešta se na stek:

Shunting Yard računanje vrednosti 22
Slika 22. - Računanje vrednosti postfiksnog izraza - korak 18. - Token za obradu smešta se na vrh steka.

Sedmi token (-), predstavlja znak operacije ....

Shunting Yard računanje vrednosti 23
Slika 23. - Računanje vrednosti postfiksnog izraza - korak 19. - Token za obradu je operator (-).

.... što znači da ponovo sledi postupak za računanje vrednosti podizraza.

Sa steka se uklanjaju dva operanda (koji ovoga puta odgovaraju operaciji oduzimanja) ....

Shunting Yard računanje vrednosti 24
Slika 24. - Računanje vrednosti postfiksnog izraza - korak 20. - Sa vrha steka biće skinuta dva operanda (3 i 8), koji odgovaraju operatoru koji je trenutno u obradi.

.... tokeni se smeštaju na pomoćne memorijske lokacije .....

Shunting Yard računanje vrednosti 25
Slika 25. - Računanje vrednosti postfiksnog izraza - korak 21. - Dva operanda sa vrha steka, smeštaju se na privremene lokacije, i nad njima se obavlja operacija (oduzimanje), koju definiše token za obradu.

.... računa se vrednost izraza ....

Shunting Yard računanje vrednosti 26
Slika 26. - Računanje vrednosti postfiksnog izraza - korak 22. - Rezultat prethodne operacije (5), smešta se u zasebnu promenljivu (koja će u sledećem koraku, u vidu tokena, biti prebačena na stek).

.... nakon čega se rezultat smešta na stek:

Shunting Yard računanje vrednosti 27
Slika 27. - Računanje vrednosti postfiksnog izraza - korak 23. - Rezultat prethodne operacije (5), prebačen je na stek.

Budući da u redu za čekanje nema više tokena, izraz je rešen ....

Shunting Yard računanje vrednosti 28
Slika 28. - Računanje vrednosti postfiksnog izraza - Krajnji rezultat (token koji predstavlja vrednost početnog izraza, nalazi se na vrhu steka).

.... a rezultat je vrednost sa vrha steka.

Pronalaženje grešaka

Kao što smo najavili još u 1. nastavku, razmotrićemo i metode za pronalaženje grešaka u izrazima.

U najosnovnijem smislu, ako sve prođe onako kako smo prikazivali do sada, izraz je ispravan, ali, ne prolazi uvek sve onako kako smo očekivali 🙂 ....

Različite greške moguće je ustanoviti u različitim fazama tumačenja izraza:

  • greške u uparivanju zagrada mogu se ustanoviti u prvom prolasku (tokom obrade infiksnog izraza)
  • greške u uparivanju operanada i operatora (višak ili manjak operanada i/ili operatora), mogu se ustanoviti tokom računanja vrednosti postfiksnog izraza

Slede primeri ....

Nedostajuća zatvorena zagrada

U infiksnom izrazu ....

		
a * (b + c
		
	
Slika 29. - Pronalaženje nedostajuće zatvorene zagrade - korak #1.

.... nedostaje zatvorena zagrada.

Tokom interpretacije izraza (u 'prvom prolasku'), nastaje sledeće stanje kontrolnih struktura:

		
RED:  [ a b c ]
STEK: [ * ( + ]
		
	
Slika 30. - Pronalaženje nedostajuće zatvorene zagrade - korak #2.

Kada algoritam stigne do poslednjeg tokena, sledi prebacivanje tokena sa steka u red, ali - pošto algoritam (u toku prebacivanja) nailazi na otvorenu zagradu - u pitanju je greška.

U opštem smislu, pojava zatvorene zagrade povlači pražnjenje steka u red - sve do pojave odgovarajuće otvorene zagrade, i stoga, posle početnog pregleda tokena iz infiksnog izraza, na steku se može naći nekolicina preostalih operatora, ali - ne može se naći otvorena zagrada.

Nedostajuća otvorena zagrada

U infiksnom izrazu ....

		
a + b * c)
		
	
Slika 31. - Pronalaženje nedostajuće otvorene zagrade - korak #1.

.... nedostaje otvorena zagrada.

Tokom interpretacije izraza (u 'prvom prolasku'), nastaje sledeće stanje kontrolnih struktura:

		
RED:  [ a b c ]
STEK: [ + * ]
		
	
Slika 32. - Pronalaženje nedostajuće otvorene zagrade - korak #2.

Kada algoritam stigne do poslednjeg tokena - koji predstavlja zatvorenu zagradu, sledi prebacivanje tokena sa steka u red, ali - pošto algoritam ne nailazi na otvorenu zagradu (na steku) - u pitanju je greška.

Same po sebi, kontrolne strukture na slici #32 su popunjene podacima koji "imaju smisla", tj. naizgled je u pitanju ispravan izraz. Međutim, iako se u ovom slučaju (i sličnim primerima) može naslutiti "gde je otvorena zagrada trebalo da stoji", najčešće nije moguće napraviti dobru pretpostavku, i stoga je najpraktičnije da algoritam samo prijavi grešku (nakon čega dovoljno iskusni korisnici mogu pokušati da ručno preprave izraze i sl).

Višak operatora

U infiksnom izrazu ....

		
a + b +
		
	
Slika 33. - Pronalaženje viška operatora - korak #1.

.... postoji višak operatora.

Nakon prvog prolaska (i pošto su operandi a i b zamenjeni npr. vrednostima 10 i 15), red za čekanje ima sledeći sadržaj:

		
RED:  [ 10 15 + + ]
STEK: [ ]
		
	
Slika 34. - Pronalaženje viška operatora - korak #2.

Nakon prvog računanja vrednosti ....

		
RED:  [ + + ]
STEK: [ 10 15 ]
		
	
Slika 35. - Pronalaženje viška operatora - korak #3.

.... nastaje sledeća situacija:

		
RED:  [ 25 + ]
STEK: [ ]
		
	
Slika 36. - Pronalaženje viška operatora - korak #4.

U narednom koraku, operand 25 se može smestiti na stek, ali, budući da bi nakon toga usledila operacija sabiranja - a pri tom je na steku prisutan samo jedan operand - u pitanju je greška.

Višak operanada

U infiksnom izrazu ....

		
a + b c
		
	
Slika 37. - Pronalaženje viška operanada - korak #1.

.... postoji višak operanada (tj. manjak operatora).

Nakon prvog prolaska (i pošto su operandi a, b i c zamenjeni vrednostima 10, 15 i 20), red za čekanje ima sledeći sadržaj:

		
RED:  [ 10 15 20 + ]
STEK: [ ]
		
	
Slika 38. - Pronalaženje viška operanada - korak #2.

Nakon prvog računanja vrednosti ....

		
RED:  [ + ]
STEK: [ 10 15 20 ]
		
	
Slika 39. - Pronalaženje viška operanada - korak #3.

.... nastaje sledeća situacija:

		
RED:  [ ]
STEK: [ 10 35 ]
		
	
Slika 40. - Pronalaženje viška operanada - korak #4.

U narednom koraku, "trebalo bi" da postoji operator koji bi pokrenuo uklanjanje operanada sa steka (i računanje vrednosti podizraza), ali, pošto takav operator ne postoji - u pitanju je greška.

Da sve bude "još gore", operator + se usput povezao sa operandima 15 i 20 (umesto 10 i 15), ali, izraz je u svakom slučaju neispravan.

Implementacija metode za računanje vrednosti postfiksnog izraza

Pošto smo se upoznali sa algoritmom za računanje vrednosti izraza, prikazaćemo i programski kod: *

		
private void RacunanjeVrednosti(Queue<char> red)
{
    Stack<Double> stek = new Stack<Double>();
    char[] tokeni = red.ToArray();
    Int32 i, d = tokeni.Length;
    
    for (i = 0; i < d;i++ )
    {
        char znak = tokeni[i];

        if (znak >= 97 && znak <= 122)
        {
            switch (znak)
            {
                case 'a': stek.Push(var_a); break;
                case 'b': stek.Push(var_b); break;
                case 'c': stek.Push(var_c); break;
                case 'd': stek.Push(var_d); break;
                case 'e': stek.Push(var_e); break;
                default: MessageBox.Show("Uneli ste neodgovarajuću promenljivu!"); return;
            }
        }
        else
        {
            Double Op2 = stek.Pop(), Op1 = stek.Pop();
            switch (znak)
            {
                case '+': stek.Push(Op1 + Op2); break;
                case '-': stek.Push(Op1 - Op2); break;
                case '*': stek.Push(Op1 * Op2); break;
                case '/': stek.Push(Op1 / Op2); break;
                default: break;
            }
        }
    }

    Double rez = stek.Pop();
    label9.Text = rez.ToString();
}
		
	
Slika 41. - Implementacija metode za računanje vrednosti postfiksnog izraza.

* I ovoga puta, prepuštamo čitaocima da samostalno (za vežbu), implementiraju funkcije za proveru validnosti izraza.

Vidimo da implementacija nije komplikovana ni u slučaju algoritma za računanje vrednosti izraza (naprotiv, reklo bi se čak da je znatno jednostavnija u odnosu na algoritam za prebacivanje izraza iz infiksne notacije u postfiksnu), i takođe se može primetiti da je složenost algoritma ponovo O(n), to jest, svaki token ponovo se rešava "u mestu".

Za kraj ....

U dva članka koje smo posvetili implementaciji algoritma Shunting Yard, nismo (zarad preglednosti) koristili sveobuhvatnije tokene (slogove ili objekte klase, koji omogućavaju da se skladište: i promenljive, i brojčane vrednosti), međutim - verujemo da će čitaoci biti u stanju da samostalno implementiraju takve tokene.

(Naravno, toplo preporučujemo da pokušate. :))

Takođe, pripremamo i članak o stablima izraza - strukturama koje nastaju preko algoritma Shunting Yard, predstavljaju izraz u apstraktnom smislu, i omogućavaju računanje vrednosti i pretvaranje strukture stabla u bilo koji oblik notacije (očekujte članak uskoro) ....

Autor članka Nikola Vukićević Za web portal codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
© 2020-2026. Sva prava zadržana.
Facebook LinkedIn Twitter Viber WhatsApp E-mail
početna > Članci > Shunting Yard - Implementacija - 2. deo - Računanje vrednosti postfiksnog izraza
codeBlog codeBlog
Sajt posvećen popularizaciji kulture i veštine programiranja.
Napomena: Tekstovi i slike na sajtu codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2020-2026. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt