Odwrotna notacja polska (ONP, ang. Reverse Polish Notation, RPN) – jest sposobem zapisu wyrażeń arytmetycznych, w którym znak wykonywanej operacji umieszczony jest po operandach (zapis postfiksowy), a nie pomiędzy nimi jak w konwencjonalnym zapisie algebraicznym (zapis infiksowy) lub przed operandami jak w zwykłej notacji polskiej (zapis prefiksowy). Zapis ten pozwala na całkowitą rezygnację z użycia nawiasów w wyrażeniach, jako że jednoznacznie określa kolejność wykonywanych działań.
ONP bardzo ułatwia wykonywanie na komputerze obliczeń z nawiasami i zachowaniem kolejności działań. Zarówno algorytm konwersji notacji konwencjonalnej (infiksowej) na odwrotną notację polską (postfiksową), jak i algorytm obliczania wartości wyrażenia danego w ONP są bardzo proste i wykorzystują stos.
Odwrotna notacja polska została opracowana przez australijskiego naukowca Charlesa Hamblina jako „odwrócenie” beznawiasowej notacji polskiej Jana Łukasiewicza na potrzeby zastosowań informatycznych. Hamblin sugerował, aby notację tę nazwać "Azciweisakul notation" (Notacja Azciweisakuł – „Łukasiewicza” pisane od tyłu).
Jest używana w niektórych językach programowania (np. FORTH, Postscript) oraz w niektórych kalkulatorach naukowych (np. Hewlett-Packard, National Semiconductor). Programy komputerowe kompilujące program dokonują analizy wyrażenia arytmetycznego, przekształcając je na ciąg instrukcji odpowiadający odwrotnej notacji polskiej. Wyrażenie to jest obliczane podczas wykonywania programu.
Spis treści |
Przykładowy konwencjonalny zapis:
(2+3)*5
w ONP wygląda tak:
2 3 + 5 *
natomiast:
((2+7)/3+(14-3)*4)/2
zapisuje się następująco:
2 7 + 3 / 14 3 - 4 * + 2 /
Przykład
Gdy wczytany element jest liczbą, to zapisujemy ją na stos. W przeciwnym wypadku należy wykonać działanie arytmetyczne na 2 ostatnich liczbach na stosie. Wartość wyrażenia znajduje się na stosie.
Wyrażenie ONP: 12 2 3 4 * 10 5 / + * +
|
Krok |
Wejście |
Stos |
|
1 |
12 |
12 |
|
2 |
2 |
2 12 |
|
3 |
3 |
3 2 12 |
|
4 |
4 |
4 3 2 12 |
|
5 |
* |
12 2 12 |
|
6 |
10 |
10 12 2 12 |
|
7 |
5 |
5 10 12 2 12 |
|
8 |
/ |
2 12 2 12 |
|
9 |
+ |
14 2 12 |
|
10 |
* |
28 12 |
|
11 |
+ |
40 |
Wartość wyrażenia: 40
Funkcja ONP:
Ten algorytm działa poprawnie jedynie dla wyrażeń, które mają pełne nawiasowanie np. (a+(b*c)).
Nie działa poprawnie, jeśli w wyrażeniu znajduje się unarny minus: (-a).
Przykład (notacja infixowa):
Po zamianie (w odwrotnej notacji polskiej):
Edsger Dijkstra wymyślił algorytm nazywany „stacją rozrządową”, ponieważ jest w działaniu bardzo podobny do kolejowej stacji rozrządowej. Tak jak algorytm liczący wartość wyrażenia ONP, ten także działa na bazie stosu. Do konwersji używane są dwie zmienne (typu ciągu znakowego) — wejście oraz wyjście. Jest także stos przechowujący operatory nie dodane jeszcze do wyjścia. W uproszczeniu, program czyta po kolei każdą literę i wykonuje operację zależną od tej litery.
Wejście 3+4*2/(1-5)^2 Przeczytaj "3" Dodaj "3" do wyjścia Wyjście: 3
Przeczytaj "+" Włóż "+" na stos Wyjście: 3 Stos: +
Przeczytaj "4" Dodaj "4" do wyjścia Wyjście: 3 4 Stos: +
Przeczytaj "*" Włóż "*" na stos Wyjście: 3 4 Stos: + *
Przeczytaj "2" Dodaj "2" do wyjścia Wyjście: 3 4 2 Stos: + *
Przeczytaj "/" Zdejmij "*" ze stosu i dodaj do wyjścia, włóż "/" na stos Wyjście: 3 4 2 * Stos: + /
Przeczytaj "("
Włóż "(" na stos
Wyjście: 3 4 2 *
Stos: + / (
Przeczytaj "1" Dodaj "1" do wyjścia Wyjście: 3 4 2 * 1 Stos: + / (
Przeczytaj "-" Włóż "-" na stos Wyjście: 3 4 2 * 1 Stos: + / ( -
Przeczytaj "5" Dodaj "5" do wyjścia Wyjście: 3 4 2 * 1 5 Stos: + / ( -
Przeczytaj ")"
Zdejmij "-" ze stosu i dodaj do wyjścia, zdejmij "(" ze stosu
Wyjście: 3 4 2 * 1 5 -
Stos: + /
Przeczytaj "^" Włóż "^" na stos Wyjście: 3 4 2 * 1 5 - Stos: + / ^
Przeczytaj "2" Dodaj "2" do wyjścia Wyjście: 3 4 2 * 1 5 - 2 Stos: + / ^
Koniec wyrażenia Zdejmij stos na wyjście Wyjście: 3 4 2 * 1 5 - 2 ^ / +
Zamiana wyrażenia algebraicznego zapisanego w notacji infiksowej na postać postfiksową (ONP)
|
Operator |
Priorytet |
|
( |
0 |
|
+ - ) |
1 |
|
* / % |
2 |
Gdy wczytany element jest:
- stałą lub nazwą zmiennej, to przesyłamy go na wyjście.
- ( to dopisujemy go na stos.
- ) to odczytaj ze stosu i prześlij na wyjście wszystkie operatory aż do nawiasu (, który należy odczytać, ale nie wysyłać na wyjście.
- +, - , *, /, % . Jeżeli priorytet operatora wczytywanego jest wyższy od priorytetu operatora znajdującego się w wierzchołku stosu lub stos jest pusty, to dopisz do stosu operator, a w przeciwnym razie odczytaj i prześlij na wyjście kolejne operatory z wierzchołka stosu o priorytecie większym lub równym priorytetowi wczytanego operatora, po czym wpisz do stosu operator.
Przykład
Wyrażenie: 12 + a * (b * c + d / e)
|
Krok |
Wejście |
Stos |
Wyjście |
|
1 |
12 |
12 |
|
|
2 |
+ |
+ |
|
|
3 |
a |
+ |
a |
|
4 |
* |
*+ |
|
|
5 |
( |
(*+ |
|
|
6 |
b |
(*+ |
b |
|
7 |
* |
*(*+ |
|
|
8 |
c |
*(*+ |
c |
|
9 |
+ |
+(*+ |
* |
|
10 |
d |
+(*+ |
d |
|
11 |
/ |
/+(*+ |
|
|
12 |
e |
/+(*+ |
e |
|
13 |
) |
/+(*+ |
/+*+ |
|
14 |
koniec |
ONP: 12 a b c * d e / + * +