Odwrotna notacja polska w praktyce
Twardy
Nawiązując do artykułu o Odwrotna notacja polska chciałbym przedstawić jak można wykorzystać ją w praktyce, na przykładzie języka C++. Nie będę się ponownie rozwodził jak się to wykonuje, bo zostało to już opisane. Cała esencja polega na tym, że nie musimy wyrażenia arytmetycznego przerabiać najpierw na RPN a później obliczać. Wszystko możemy wykonać za jednym zamachem. Przykładowo mamy równanie:
2*2+2
W programie wykorzystujemy dwa stosy - jeden na liczby (najlepiej zmiennoprzecinkowe), drugi na znaki arytmetyczne. I teraz od lewej bierzemy dwójkę na stos z liczbami, dalej znak mnożenia na stos ze znakami arytmetycznymi, znowu dwa na stos i dalej znak plus. Na stosie ze znakami jest mnożenie, czyli wyższy priorytet od dodawania. Tak więc zdejmujemy mnożenie, wykonujemy na liczbach ze stosu i znowu wynik odkładamy ponownie.
Dalej dwa znowu na stos i koniec. Więc zdejmujemy kolejno wszystkie znaki a że mamy tylko jeden (dodawanie), to sumujemy wynik mnożenia odłożony na stosie z ostatnia dwójką.
Nie rozwlekałem się dokładniej nad tym zadaniem, gdyż opis i stosowaną terminologie można zobaczyć we wspomnianym artykule.
typedef double typ_danej;
typ_danej oblicz(string &s) {
typ_danej liczba (0);
char stos_char[count_char];
char last_;
int pos_stos_char (0);
typ_danej stos_liczba[count_char * 2];
int pos_stos_liczba (0);
while (s.length()>0) {
if (isdigit(s[0])) {
liczba = atof(s.c_str());
while (isdigit(s[0]) || s[0]=='.') s.erase(0,1);
stos_liczba[pos_stos_liczba] = liczba;
pos_stos_liczba++;
} else {
if (s[0]!=')') {
if (pos_stos_char>0) {
last_ = stos_char[pos_stos_char - 1];
} else last_ = '#';
} else last_ = ')';
while(priorytet(s[0],last_)==0) {
if (przelicz(last_,&stos_liczba[pos_stos_liczba],pos_stos_liczba)==1) {
DivideByZero = true;
return 0;
}
pos_stos_char--;
if (pos_stos_char>0) {
last_ = stos_char[pos_stos_char - 1];
} else last_ = '#';
}
if (last_!=')') {
stos_char[pos_stos_char] = s[0];
pos_stos_char++;
} else {
while (stos_char[pos_stos_char-1]!='(') {
if (przelicz(stos_char[pos_stos_char-1], &stos_liczba[pos_stos_liczba],pos_stos_liczba)==1) {
DivideByZero = true;
return 0;
}
pos_stos_char--;
}
pos_stos_char--;
}
s.erase(0,1);
}
}
while (pos_stos_char>0) {
if (przelicz(stos_char[pos_stos_char-1], &stos_liczba[pos_stos_liczba],pos_stos_liczba)==1) {
DivideByZero = true;
return 0;
}
pos_stos_char--;
}
return stos_liczba[pos_stos_liczba-1];
}
Argumentem funkcji jak widać jest dana typu string, w której opisane jest równanie. Są oczywiście odnośniki do innych funkcji, które można przeanalizować ściągając całość (link niżej).
Jest to dość prosty kalkulator, gdyż operuje na następnych znakach arytmetycznych: + - * / (potęga, która też może być pierwiastkowaniem - np. pierwiastek trzeciego stopnia z 27 zapiszemy: 27(1/3), czyli 27 do potęgi jednej trzeciej) oraz nawiasy.
W załączniku dodałem kompletny kod źródłowy, gdzie dodatkowo jest funkcja do weryfikacji równania, wraz z kodem wynikowym w postaci pliku wykonywalnego. Oparte jest to na konsoli a sam program pisałem pod kompilator Dev-C++. Kod można ściągnąć stąd.
ooo starocie. Ale wtedy nerwowy człowiek był. Wszedłem bo zauważyłem w logach, że było odwołanie do mojego serwera ze skutkiem 404. Usunąłem jakiś czas temu dział soft bo myślałem, że już nie potrzebny :D. W najbliższym czasie naprawie problem z linkiem.
Twardy, polecam wziąć udział w Olimpiadzie Zaciemniania Kodu - masz duże szanse na przyzwoite miejsce. A tak na marginesie:
Poza tym jaki jest sens optymalizować algorytm, który operuje na łańcuchach, które mają od kilku do kilkudziesięciu znaków ? Człowieku zyskasz najwyżej nanosekundę (może dwie) - kosmicznie dłużej trwać będzie dwukrotne kliknięcie w celu uruchomienia Twojego programu. W readme.txt trzeba będzie dopisywać aby użytkownik szybko klikał bo inaczej optymalizacje nie będą miały sensu. Optymalizować trzeba gdy jest potrzeba :).
Zgadzam się z Heimdallem. Ja próbuje dodać parę funkcji (chciałem dodać arcusy i funkcje hiperboliczne) i to wcale nie jest takie proste... Ale jak ktoś ma może program w którym są te funkcje to proszę o kontakt;)
Pozdrawiam...:)
"A to, że chcesz go wykorzystać w taki sposób świadczy tylko o Tobie, więc daruj sobie tego typu komentarze i nikt dla Ciebie nie będzie go przerabiał."
Daruj sobie tego typu teksty, kod powinien być w miarę przystosowany do przeróbek i uniwersalny. Jeżeli mówisz, że jest to kod wyłącznie edukacyjny to po jaką cholerę optymalizujesz go? Przez optymalizację tylko zaciemniłeś istotę ONP.
Programam ma jednak wady i nie idzie go tak łatwo rozbudować o inne funkcje ktore zaczynaja sie z nazwy na te same litery :(
Poprawione...Troche się namęczyłem nim skumałem sposób dodawania linków ale działa.
BTW: w podglądzie linki dodane poprzez są niebieskie czyli tak jakby nie istniały. Należało by to zmienic bo to wprowadza w błąd
Sugeruję odnaleźć artykuł "odwrotnej notacji polskiej (RPN)" i podmienić link, który widnieje tu w starym zapisie i nigdzie już nie prowadzi.
Ja nie stworzyłem programu, żeby sobie ściągać, przerabiać i podpisywać się pod nim. Ja pokazałem jak to działa - jest to przykład jak wykorzystać odwrotną notacje polską w praktyce. Czy tak nie brzmi temat? A to, że chcesz go wykorzystać w taki sposób świadczy tylko o Tobie, więc daruj sobie tego typu komentarze i nikt dla Ciebie nie będzie go przerabiał.
To nie zrozumiales do konca. Program za jednym zamachem zamienia na odwrotna notacje i od razu oblicza - jak sie oczywiscie da, ze wzgledu na pobierane priorytety znakow. Nie robi on calkowitej onp i pozniej ja oblicza, tylko wszystko za jednym razem aby zyskac na czasie. W poRZądku?
pobieranie ze stosu wszystkiego i wykonywanie działań o wyższym priorytecie jest w pożądku, ale co to ma wspólnego z odwrotną notacją polską???
może się mylę, ale wydaje mi się, ze odwrotna notacja polska polega na tym, że na stos się wkłada już w odpowieniej koleności