Odwrotna Notacja Polska - część 2

W poprzedniej części (Odwrotna Notacja Polska - część 1) dokonaliśmy zamiany wyrażenia w formie infiksowej na postać postfiksową (czyli formę w Odwrotnej Notacji Polskiej). W tym wpisie spróbujemy dokonać sprawdzenia poprawności nawiasów występujących w wyrażeniu infiksowym oraz obliczymy wartość wyrażenia postfiksowego. Do sprawdzenia poprawności nawiasów w wyrażeniu infiksowym użyjemy stosu (więcej na ten temat - Sprawdzanie poprawności nawiasów). Jeśli wyrażenie nie będzie poprawne pod wartość wyrażenia postfiksowego podstawimy odpowiedni napis (klasa String - "Niepoprawne nawiasy w wyrażeniu infiksowym"). Nasza metoda sprawdzająca poprawność nawiasów będzie metodą prywatną:

01.// metoda sprawdza poprawność nawiasów w wyrażeniu infiksowym
02.private boolean czyPoprawneNawiasy() {
03. // tworzymy stos
04. Stack<String> stos = new Stack<String>();
05. 
06. // dzielimy wyrażenie infiksowe na części na podstawie separatorów
07. StringTokenizer st = new StringTokenizer(wyrazenieInfiksowe, "()", true);
08.         
09. // dopóki są elementy w wyrażeniu wejściowym
10. while(st.hasMoreTokens()) {
11.  // pobieramy element
12.  String s = st.nextToken();
13.             
14.  // jeżeli znak jest nawiasem otwierającym odkładamy go na stos
15.  if(s.equals("(")) stos.push(s);
16.  // jeżeli znak jest nawiasem zamykającym porównujemy go z wartością ze stosu           
17.  if(s.equals(")")) {
18.   if (stos.isEmpty()) return false;
19.   if (!stos.pop().equals("(")) return false;
20.  }
21. }
22. // zwracamy true albo false (stos pusty - wszystkie nawiasy są poprawne)
23. return stos.isEmpty();        
24.}
Po sprawdzeniu poprawności nawiasów możemy już dokonać obliczenia wartości wyrażenia postfiksowego według następującego algorytmu:
  • Inicjalizujemy stos,
  • Analizujemy wyrażenie postfiksowe od lewej do prawej strony,
  • Jeżeli element jest argumentem to odkładamy go na stos,
  • Jeżeli element jest operatorem to ściągamy ze stosu dwie wartości, wykonujemy działanie, odkładamy na stos wynik,
  • Jeżeli dotarliśmy do końca wyrażenie postfiksowego, ściągamy wartość ze stosu (jest to obliczona wartość wyrażenia postfiksowego).
Przykład obliczenia wartości wyrażenia postfiksowego:
Wyrażenie infiksowe: (4*2)+(15/3)
Wyrażenie postfiksowe: 4 2 * 15 3 / + 

kolejny znak stos operacja
     4          4       odkładamy na stos
     2          4 2     odkładamy na stos
     *          8       ściągamy ze stosu dwie wartości i obliczamy
                        wartość wyrażenia(4*2), odkładamy ją na stos
     15         8 15    odkładamy na stos
     3          8 15 3  odkładamy na stos
     /          8 5     ściągamy ze stosu dwie wartości i obliczamy
                        wartość wyrażenia(15/3), odkładamy ją na stos
     +          13      ściągamy ze stosu dwie wartości i obliczamy
                        wartość wyrażenia(8+5), odkładamy ją na stos
                        ściągamy ze stosu wynik końcowy (13)

Obliczona wartość wyrażenia postfiksowego: 13
Poniżej metoda obliczająca wartość wyrażenia postfiksowego. Operujemy na wartościach typu double.
01.// metoda oblicza wartość wyrażenia postfiksowego
02.private double oblicz() {
03.   
04. // tworzymy pusty stos
05. Stack<Double> stos = new Stack<Double>();
06.         
07. // dzielimy wyrażenie postfiksowe na elementy na podstawie spacji
08. StringTokenizer st = new StringTokenizer(wyrazeniePostfiksowe, " ");
09.         
10. // dopóki są elementy w wyrażeniu wejściowym
11. while(st.hasMoreTokens()) {
12.  // pobieramy element
13.  String s = st.nextToken();
14.   
15.  // jeśli element nie jest operatorem (czyli jest wartością)
16.  if (!s.equals("+") && !s.equals("*") && !s.equals("-") && !s.equals("/")) {
17.   // zamieniamy łańcuch na liczbę
18.   double wartosc = Double.parseDouble(s);
19.   // odkładamy wartość na stos
20.   stos.push(wartosc);
21.  }
22.  else {
23.   //  jeśli element jest operatorem ściągamy dwie wartości ze stosu
24.   double wartosc1 = stos.pop();
25.   double wartosc2 = stos.pop();
26.   // w zależności od operatora obliczamy wynik i odkładamy go na stos
27.   switch(s.charAt(0)) {
28.    case '*': {stos.push(wartosc2 * wartosc1); break;}
29.    case '+': {stos.push(wartosc2 + wartosc1); break;}
30.    case '-': {stos.push(wartosc2 - wartosc1); break;}
31.    case '/': {stos.push(wartosc2 / wartosc1); break;}
32.   }
33.  }
34. }
35. // zwracamy końcowy wynik
36. return stos.pop();
37.}
Cała klasa OdwrotnaNotacjaPolska:
001.// wykorzystujemy klasy Scanner, Stack<E> oraz StringTokenizer z pakietu java.util
002.import java.util.*; 
003./**
004. * Odwrotna Notacja Polska
005. * Zamiana wyrażeń infiksowych na postfiksowe
006. * @author kodatnik.blogspot.com
007. */
008.public class OdwrotnaNotacjaPolska {
009. // pola klasy
010. private String wyrazenieInfiksowe;
011. private String wyrazeniePostfiksowe;
012. private double wynik;  
013.  
014. // konstruktor dokonuje przypisania polom wyrażeń
015. public OdwrotnaNotacjaPolska(String wyrazenie) {
016.  this.wyrazenieInfiksowe = wyrazenie;
017.  wyrazeniePostfiksowe = "";
018.  wynik = 0.0;
019.  if(!czyPoprawneNawiasy())
020.   wyrazeniePostfiksowe = "Niepoprawne nawiasy w wyrażeniu infiksowym";
021.  else {
022.   // wywołujemy metodę dokonującą konwersji
023.   infiksNaPostfiks();
024.   wynik = oblicz();
025.  }  
026. }
027. 
028. // metoda zwraca wyrażenie w formie infiksowej
029. public String pobierzWyrazenieInfiksowe() {
030.  return wyrazenieInfiksowe;
031. }
032. 
033. // metoda zwraca wyrażenie w formie postfiksowej
034. public String pobierzWyrazeniePostfiksowe() {
035.  return wyrazeniePostfiksowe;
036. }
037.  
038. // metoda zwraca obliczoną wartość wyrażenia postfiksowego
039. public double pobierzWynik() {
040.  return wynik;
041. }
042.  
043. // metoda sprawdza poprawność nawiasów w wyrażeniu infiksowym
044. private boolean czyPoprawneNawiasy() {
045.  // tworzymy stos
046.  Stack<String> stos = new Stack<String>();
047. 
048.  // dzielimy wyrażenie infiksowe na części na podstawie separatorów
049.  StringTokenizer st = new StringTokenizer(wyrazenieInfiksowe, "()", true);
050.         
051.  // dopóki są elementy w wyrażeniu wejściowym
052.  while(st.hasMoreTokens()) {
053.   // pobieramy element
054.   String s = st.nextToken();
055.             
056.   // jeżeli znak jest nawiasem otwierającym odkładamy go na stos
057.   if(s.equals("(")) stos.push(s);
058.   // jeżeli znak jest nawiasem zamykającym porównujemy go z wartością ze stosu           
059.   if(s.equals(")")) {
060.    if (stos.isEmpty()) return false;
061.    if (!stos.pop().equals("(")) return false;
062.   }
063.  }
064.  // zwracamy true albo false (stos pusty - wszystkie nawiasy są poprawne)
065.  return stos.isEmpty();        
066. }
067. 
068. // metoda konwertuje wyrażenie w postaci infiksowej na postfiksową
069. // wynik operacji jest zapisywany w polu wyrazeniePostfiksowe
070. private void infiksNaPostfiks() {
071.         
072.  // tworzymy pusty stos
073.  Stack<String> stos = new Stack<String>();
074.         
075.  // dzielimy wyrażenie infiksowe na części na podstawie separatorów
076.  StringTokenizer st = new StringTokenizer(wyrazenieInfiksowe, "+-*/()", true);
077.         
078.  // dopóki są elementy w wyrażeniu wejściowym
079.  while(st.hasMoreTokens()) {
080.   // pobieramy element
081.   String s = st.nextToken();
082.          
083.   // jeżeli element jest operatorem
084.   if( s.equals("+") || s.equals("*") || s.equals("-") || s.equals("/")) {
085.    // sprawdzamy priorytety
086.    while(!stos.empty() && priorytet(stos.peek()) >= priorytet(s))
087.     wyrazeniePostfiksowe += stos.pop()  + " ";
088.    // odkładamy operator na stos
089.    stos.push(s);
090.   }
091.   // jeżeli element jest nawiasem otwierającym
092.   else if(s.equals("(")) stos.push(s);
093.   // jeżeli element jest nawiasem zamykającym
094.   else if(s.equals(")")) {
095.    // ściągamy operatory ze stosu, aż do nawiasu otwierajęcego
096.    while(!stos.peek().equals("(")) wyrazeniePostfiksowe += stos.pop() + " ";
097.    // ściągamy nawias otwierający
098.    stos.pop();
099.   }
100.   // jeżeli element nie jest operatorem ani nawiasem dodajemy go do wyrażenia postfiksowego
101.   else wyrazeniePostfiksowe += s  + " ";
102.  }
103.  // ściągamy ze stosu pozostałe operatory i dodajemy je do wyrażenia postfiksowego
104.  while(!stos.empty()) wyrazeniePostfiksowe += stos.pop()  + " ";
105.         
106. }
107.   
108. // metoda zwraca priorytet operatora
109. public static int priorytet(String operator) {
110.  // dla '+' i '-' zwracamy 1
111.  if(operator.equals("+") || operator.equals("-")) return 1;
112.  // dla '*' i '/' zwracamy 2
113.  else if(operator.equals("*") || operator.equals("/")) return 2;
114.  // dla pozostałych 0
115.  else return 0;
116. }
117.  
118. // metoda oblicza wartość wyrażenia postfiksowego
119. private double oblicz() {
120.   
121.  // tworzymy pusty stos
122.  Stack<Double> stos = new Stack<Double>();
123.         
124.  // dzielimy wyrażenie postfiksowe na elementy na podstawie spacji
125.  StringTokenizer st = new StringTokenizer(wyrazeniePostfiksowe, " ");
126.         
127.  // dopóki są elementy w wyrażeniu wejściowym
128.  while(st.hasMoreTokens()) {
129. 
130.   // pobieramy element
131.   String s = st.nextToken();
132.   
133.   // jeśli element nie jest operatorem (czyli jest wartością)
134.   if (!s.equals("+") && !s.equals("*") && !s.equals("-") && !s.equals("/")) {
135.    // zamieniamy łańcuch na liczbę
136.    double wartosc = Double.parseDouble(s);
137.    // odkładamy wartość na stos
138.    stos.push(wartosc);
139.   }
140.   else {
141.    //  jeśli element jest operatorem ściągamy dwie wartości ze stosu
142.    double wartosc1 = stos.pop();
143.    double wartosc2 = stos.pop();
144.    // w zależności od operatora obliczamy wynik i odkładamy go na stos
145.    switch(s.charAt(0)) {
146.     case '*': {stos.push(wartosc2 * wartosc1); break;}
147.     case '+': {stos.push(wartosc2 + wartosc1); break;}
148.     case '-': {stos.push(wartosc2 - wartosc1); break;}
149.     case '/': {stos.push(wartosc2 / wartosc1); break;}
150.    }
151.   }
152.  }
153.  // zwracamy końcowy wynik
154.  return stos.pop();
155. }
156.  
157. // metoda zwraca nam łańcuch tekstowy z wyrażeniem
158. // w formie postfiksowej, oraz wynik
159. public String toString() {
160.  return wyrazeniePostfiksowe + "\nWynik: " + wynik;
161. }
162.}
Poniżej przykład wykorzystania klasy.
01.public class TestONP {
02. public static void main(String args[]) {
03. 
04.  Scanner sc = new Scanner(System.in);
05.  System.out.print ("Podaj wyrażenie infiksowe: " );
06.   
07.  // pobieramy od użytkownika wyrażenie
08.  String wyrazenie = sc.nextLine();
09. 
10.  // tworzymy nowy obiekt klasy OdwrotnaNotacjaPolska
11.  // i przekazujemy do konstruktora pobrane od użytkownika wyrażenie
12.  OdwrotnaNotacjaPolska onp = new OdwrotnaNotacjaPolska(wyrazenie);
13. 
14.  // wyświetlamy wyrażenie w postaci postfiksowej
15.  System.out.println ("Wyrażenie postfiksowe: " + onp);
16. }
17.}
Wyniki działania aplikacji:
Podaj wyrażenie infiksowe: (4*2)+(15/3)
Wyrażenie postfiksowe: 4 2 * 15 3 / + 
Wynik: 13.0
Podaj wyrażenie infiksowe: (3+4)*2)
Wyrażenie postfiksowe: Niepoprawne nawiasy w wyrażeniu infiksowym
Podaj wyrażenie infiksowe: ((1+2)-2.5)*8/2
Wyrażenie postfiksowe: 1 2 + 2.5 - 8 * 2 / 
Wynik: 2.0
Nasza klasa nie obsługuje poprawnie sytuacji wyjątkowych oraz ma małą liczbę operatorów (można się pokusić o dodanie operacji potęgowania, pierwiastkowania czy też funkcji trygonometrycznych).


1 Komentarz - Odwrotna Notacja Polska - część 2

obiady domowe z dowozem Jarocin pisze...

bardzo lubieten blog

Prześlij komentarz

Możesz użyć niektórych tagów HTML, takich jak <b>, <i>, <u>, <a> Nie spamuj :)

Popularne posty