Przy zapisie obliczeń w odwrotnej notacji polski wykorzystujemy zapis prefiksowy, a nie tradycyjnie infiksowy. Różnica polega na ustawieniu liczb oraz operatorów. Tradycyjny zapis infiksowy to np. 2 + 3 - operator znajduje się pomiędzy dwoma liczbami (lub wyrażeniami). W przypadku postaci prefiksowej najpierw podajemy liczby, potem operator: 2 3 +. Na pierwszy rzut oka wydaje się to mało intuicyjne, ale taki zapis ułatwia komputerowi obliczenia, ponieważ w ONP nie występują nawiasy, ale kolejność wykonywania działań zostaje zachowana. Implementację ONP najlepiej oprzeć o zasadę działania stosu.
W celu zobrazowania zasady obliczania takich wyrażeń przeanalizujmy poniższy przykład, który wyjaśnia jak obliczyć wyrażenie "8 4 2 + 3 - *".
Stos | Wejście | Operacja |
---|---|---|
(pusty) | 8 | Kładziemy liczbę na stosie |
{8} | 4 | Kładziemy liczbę na stosie |
{8, 4} | 2 | Kładziemy liczbę na stosie |
{8, 4, 2} | + | Operator dodawania jest dwuargumentowy. Zdejmujemy dwie wartości ze stosu 2 i 4. I ich sumę odkładamy z powrotem na stos. |
{8, 6} | 3 | Kładziemy liczbę na stosie |
{8, 6, 3} | - | Operator odejmowania. Zdejmujemy wartości ze stosu: 3 i 6. I na stos odkładamy wynik działania. |
{8, 3} | * | Operator mnożenia. Zdejmujemy wartości ze stosu, kolejno 3 i 8. Wynik odkładamy na stos. |
{24} | (brak) | Jako wynik zwracamy wartość na stosie |
W notacji nawiasowej obliczane wyrażenie miałoby postać: ((2 + 4) - 3)·8 = (6 - 3)·8 = 3·8 = 24.
Implementacja kodu będzie korzystać ze stosu, który został przeze mnie opisany tutaj. Istnieje możliwość dołączenia mojego rozwiązania do swojego projektu pobierając i dołączając do swojej aplikacji biblioteke stos.h, którą dołączamy do aplikacji przy pomocy include:
Tym razem zaczniemy od funkcji main. Wczytamy od użytkownika wyrażenie, a następnie wywołamy jeszcze nienapisaną funkcję ONP i wyświetlimy wynik. Zakładamy, że napis będzie składał się z liczb, operatorów oraz spacji.
Przykładowo dla:
Jak już ustaliliśmy dostajemy ciąg znaków. Zakładamy, że wynik to będzie liczba rzeczywista. Nasz program będzie iterował każdy znak i odpowiednio go interpretował. Nie będziemy sprawdzać poprawności wejścia. Możemy założyć, że użytkownik umie korzystać z ONP bezbłędnie.
Na początku tworzymy nowy stos na który będziemy odkładać liczby. (3.) Zmienna i będzie nam wskazywała, który znak aktualnie interpretujemy. Interpretacja będzie odbywała się (4.) w pętli while.
Załóżmy, że chcemy wczytać liczbę. Musimy rozpatrzeć dwa przypadki: kiedy liczba jest ujemna - zaczyna się wtedy od znaku "-", potem cyfry. W drugim przypadku same cyfry.
Wprowadźmy dodatkową zmienną minus, która będzie pamiętała czy wczytany napis ma być ujemny czy nie. Domyślnie minus = false, ponieważ zakładamy, że liczba jest dodatnia. Bardzo ważna jest linijka (7.) gdzie zwiększamy i. Bez niej nasz program dalej przestałby wczytywać liczbę ponieważ znalazłby znak "-" zamiast cyfr.
Dalsza część to wczytywanie liczby dopóki znajdujemy jakiekolwiek cyfry.
Zmienna a przechowuje wczytywaną liczbę, ale bez znaku. Przed położeniem jej na stosie musimy pomnożyć przez -1 jeśli ma być ujemna.
Jeśli okazuje się, że to nie wczytujemy liczby to zakładamy, że mamy do czynienia z operatorem. W tej części pobierzemy do zmiennych t1 i t2 dwie liczby z wierzchu stosu, a przy użyciu switch'a wybierzemy odpowiednią operację do wykonania:
Na sam koniec zwracamy wynik zdejmując wynik ze stosu:
Wynik (39.) musimy pobrać przed (40.) usunięciem stosu, bo inaczej stracimy do niego dostęp, a stos wraz z wynikiem będzie zalegał w pamięci. Na sam koniec funkcji (42.) zwracamy wynik.
Działanie funkcji można sprawdzić przy pomocy poniższego fragmentu kodu:
Zmienna BUFFER_SIZE jest opcjonalna. Wygodniej się zmienia rozmiar buforu w jednym, a nie w dwóch miejscach kiedy uznamy, że użytkownik powinien móc wprowadzić dłuższy ciąg znaków.
Dodaj kolejny operator potęgowania ^.
Dodaj intepretację liczb, które mają na początku +, np. +123.