Kalkulator
-- Sebastian Pawlak, 2002.
Prezentuję tu program, którego zadaniem jest obliczanie wartości wyrażeń
arytmetycznych. Obsługiwane są operatory arytmetyczne (^ * / + -), przy czym
najwyższy priorytet ma operator ^, następnie * /, następnie + -. Łączność
operatorów jest lewostronna. Argumentami mogą być liczby rzeczywiste
nieujemne. Wyrażenie zapisane w notacji nawiasowej konwertowane jest
do tożsamego zapisanego w Odwrotnej Notacji Polskiej. Następnie wyrażenie
w ONP obliczane jest z wykorzystaniem stosu.
Myślę, że warty uwagi jest algorytm przekształcania wyrażenia zapisanego
w notacji nawiasowej do wyrażenia zapisanego w Odwrotnej Notacji Polskiej:
Krok 1
Jeżeli nie ma więcej znaków w ciągu wejściowym, to realizuj krok 6; w przeciwnym razie, przeczytaj następny element (nawias, operator lub argument) i nazwij go x.
Krok 2
Jeżeli x jest argumentem, to zapisz go w ciągu wyjściowym, umieść po nim spację i przejdź do realizacji kroku 1.
Krok 3
Jeżeli x jest nawiasem otwierającym, wpisz go na stos i realizuj krok 1. Jeżeli x jest nawiasem zamykającym, przejdź do realizacji kroku 4, a w przeciwnym razie - kroku 5.
Krok 4
Jeżeli na wierzchu stosu nie ma nawiasu otwierającego, to wycofaj ten obiekt ze stosu wpisując go do ciągu wyjściowego, a zaraz za nim wpisz spację i przejdź do wykonywania kroku 4. Jeżeli na wierzchołku stosu jest nawias otwierający, to wycofaj go ze stosu i przejdź do realizacji kroku 1.
Krok 5
W przypadku, gdy priorytet elementu wierzchołkowego stosu jest większy lub równy priorytetowi x, wycofaj element z wierzchołka stosu, wpisz go do ciągu wyjściowego, a potem wpisz za nim znacznik i powtórz krok 5. W przeciwnym razie, połóż x na wierzchołek stosu i wróć do wykonywania kroku 1.
Krok 6
Opróżnij stos po jednym elemencie, wpisując każdy z nich do ciągu wyjściowego i wstawiając między nimi spację; i to już koniec.
Kod źródłowy pliku "stos.h":
/* stos.h: plik naglowkowy biblioteki z implementacja stosu. * Sebastian Pawlak, 2002 */ #ifndef _STOS_H #define _STOS_H void push(double); double pop(void); #endif
Kod źródłowy pliku "stos.c":
/* stos.c: biblioteka z implementacja stosu. * Sebastian Pawlak, 2002 */ #include <stdio.h> #include "stos.h" #define MAX_STOS 512 /* maksymalny rozmiar stosu */ double stos[MAX_STOS]; int sp = 0; /* wskaznik kolejnego wolnego miejsca na stosie */ /* push: wstawia element na stos */ void push(double element) { if (sp < MAX_STOS) stos[sp++] = element; else printf("push: Przepelnienie stosu!\n"); } /* pop: zdejmuje element ze stosu */ double pop(void) { if (sp > 0) return stos[--sp]; return 0; }
Kod źródłowy pliku "kalkulator.c":
/************************** * K A L K U L A T O R * * Sebastian Pawlak, 2002 * **************************/ #include <stdio.h> #include <math.h> /* dla pow() */ #include <string.h> /* dla strlen() */ #include <stdlib.h> /* dla atoi() */ #include "stos.h" #define MAX 1024 /* maksymalna dlugosc wyrazenia (OdwNotPl) w znakach */ void konwerterOdwNotPl(char *, char *); double oblicz(char *); int main(void) { char wyrazenie[MAX], wyrazenieOdwNotPl[MAX]; printf("Podaj wyrazenie arytmetyczne:\n"); scanf("%s", wyrazenie); konwerterOdwNotPl(wyrazenieOdwNotPl, wyrazenie); printf("Wynik konwersji do Odwrotnej Notacji Polskiej: %s\n", wyrazenieOdwNotPl); printf("Wynik: %.5g\n", oblicz(wyrazenieOdwNotPl)); return 0; } /* konwerterOdwNotPl: funkcja przeksztalca wyrazenie zapisane w notacji * nawiasowej do wyrazenia w Odwrotnej Notacji Polskiej. * * Obslugiwane sa operatory: ^ * / + - */ void konwerterOdwNotPl(char *wynik, char *s) { char x, y; while (x = *s++) { /* krok 1 */ if ((x >= '0' && x <= '9') || x == '.') { /* krok 2 */ /* kopiowanie calej liczby z wejscia na wyjscie */ do *wynik++ = x; while (x = *s, ((x >= '0' && x <= '9') || x == '.') && s++); *wynik++ = ' '; } else if (x == '(') /* krok 3 */ push((double)'('); else if (x == ')') /* krok 4 */ while ((y = (char)pop()) != '(') *wynik++ = y, *wynik++ = ' '; else { /* krok 5 */ while (y = (char)pop(), y && (y == '^' || (y == '*' || y == '/') && x != '^' || (y == '+' || y == '-') && x != '*' && x != '/' && x != '^')) *wynik++ = y, *wynik++ = ' '; push((double)y); push((double)x); } } while(x = (char)pop()) /* krok 6 */ *wynik++ = x, *wynik++ = ' '; *wynik = '\0'; /* zakonczenie lancucha wyjsciowego */ } /* oblicz: funkcja oblicza wartosc wyrazenia arytmetycznego zapisanego w * Odwrotnej Notacji Polskiej */ double oblicz(char *s) { char element[MAX]; double op2; while (sscanf(s, "%s", element) != -1) { s += strlen(element) + 1; switch (element[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': /* liczba */ push(atof(element)); break; case '^': op2 = pop(); /* ze wzgledu na nieprzemiennosc potegowania */ push(pow(pop(), op2)); break; case '*': push(pop() * pop()); break; case '/': op2 = pop(); /* ze wzgledu na nieprzemiennosc dzielenia */ if (op2 != 0.0) push(pop() / op2); else { printf("oblicz: Blad dzielenia przez zero!\n"); return -1; } break; case '+': push(pop() + pop()); break; case '-': op2 = pop(); /* ze wzgledu na nieprzemiennosc odejmowania */ push(pop() - op2); break; default: printf("oblicz: Blad - nieznany znak!\n"); return -1; } } return pop(); /* zwraca wynik */ }