![]() |
![]() |
![]()
Post
#1
|
|
![]() Grupa: Zarejestrowani Postów: 78 Pomógł: 9 Dołączył: 2.02.2011 Skąd: undefined Ostrzeżenie: (0%) ![]() ![]() |
Witam.
Chciałem sobie zrobić prosty kalkulator który będzie pobierał od użytkownika wyrażenie arytmetyczne i zwracał jego wynik. Np: Podaję: Kod (10+9)/(11*8) Otrzymuję Kod 0.215909090 Myślałem nad wykorzystaniem do tego wyrażeń regularnych, ale pojawia się pewien problem. Otóż nie bardzo wiem jak wymusić na kwantyfikatorach, aby dzielił mi wyrażenie tylko względem najwyższego znaku tzn. powyższe wyrażenie powinno mi podzielić na: Kod l=(10+9); r=(11*8); sign=/; oczywiście powyższe wyrażenia mogą wyglądać także inaczej. Np można używać na nich prostych funkcji: Kod sin(10+4)/cos(8-11) l=sin(10+4); sign=/; r=cos(8-11); Do tej pory używałem wyrażenia regularnego: Kod /^(?<l>[[:print:]]+)(?<sign>[\*\/\-\+]{1})(?<r>[[:print:]]+)/ Każdą ze stron wyrażenia arytmetycznego będę rozpoznawał rekurencyjnie, i do analizy funkcji mam już wyrażenie regularne Jak można by to najrozsądniej rozwiązać? Ten post edytował shinuexx 27.11.2011, 14:10:33 |
|
|
![]() |
![]()
Post
#2
|
|
![]() Grupa: Zarejestrowani Postów: 6 476 Pomógł: 1306 Dołączył: 6.08.2006 Skąd: Kraków Ostrzeżenie: (0%) ![]() ![]() |
Wyrażenia regularne kompletnie nie nadają się do czegoś takiego, tutaj powinieneś wykorzystać normalny parser wyrażeń matematycznych (taki który najpierw rozbija ciąg na tokeny, a następnie tworzy z nich drzewo wyrażeń):
Kod sin(10+4)/cos(8-11) Dzięki temu będziesz mieć możliwość wyłapywania błędów składni, ustalania pierwszeństwa operatorów itd.
Tokeny: sin, (, 10, +, 4, ), /, cos, (, 8, -, 11, ) Drzewo wyrażeń: "/" / \ sin cos | | 10 + 4 8 - 11 Ten post edytował Crozin 27.11.2011, 14:07:31 |
|
|
![]()
Post
#3
|
|
![]() Grupa: Zarejestrowani Postów: 78 Pomógł: 9 Dołączył: 2.02.2011 Skąd: undefined Ostrzeżenie: (0%) ![]() ![]() |
Czyli analizuję ciąg od początku, sprawdzam co mam a potem czego oczekuję następnie. Jeśli tak nie jest to błąd a jeśli jest to następny znak?
|
|
|
![]()
Post
#4
|
|
![]() Grupa: Zarejestrowani Postów: 6 476 Pomógł: 1306 Dołączył: 6.08.2006 Skąd: Kraków Ostrzeżenie: (0%) ![]() ![]() |
Pogoogleaj sobie za "writing parser c / java" czy coś w ten deseń. Jest masa materiałów, całość nie jest trudna (szczególnie gdy mówimy o wyrażeniach matematycznych) ale dosyć obszerna.
Ewentualnie jak z czymś konkretnym będziesz mieć problem, pytaj. |
|
|
![]()
Post
#5
|
|
![]() Grupa: Zarejestrowani Postów: 78 Pomógł: 9 Dołączył: 2.02.2011 Skąd: undefined Ostrzeżenie: (0%) ![]() ![]() |
W sumie to nie bardzo już teraz wiem co miałem znaleźć. O LEXER'ach i PARSER'ach??
ok. Dowiedziałem się od znajomego, że można to zrobić za pomocą odwrotnej notacji polskiej. Jak dla mnie jest to naprawdę przyjemny i przejrzysty algorytm liczenia wyrażeń matematycznych uwzględniający pierwszeństwo. Wyrażeń regularnych użyłem do dzielenia ciągu na tokeny.
Nie miałem innego pomysłu na token'owanie a to było dość przyjemne. Jeśli ktoś miałby lepszy pomysł to nie mam nic lepszego. Jeśli ktoś chce mogę zamieścić kody tego co do tej pory udało mi się zrobić tj: -lexer, -parser, -counter, wraz z obsługą podstawowych funkcji oraz podstawiania zmiennych zdefiniowanych przed wyliczeniem. Brak jeszcze obsługi błędów, co zamierzam wprowadzić. |
|
|
![]()
Post
#6
|
|
![]() Grupa: Zarejestrowani Postów: 6 476 Pomógł: 1306 Dołączył: 6.08.2006 Skąd: Kraków Ostrzeżenie: (0%) ![]() ![]() |
Cytat Nie miałem innego pomysłu na token'owanie a to było dość przyjemne. Jeśli ktoś miałby lepszy pomysł to nie mam nic lepszego. Generalnie użycie wyr. reg. nawet do wydzielenia tokenów jest błędne - bo one wyłapią jedynie pewne wzorce, a nie zwrócą rezultatu pełnej analizy ciągu (jeżeli coś nie będzie pasować to to ominą zamiast rozpoznać jako niewłaściwy token).Nie mniej jednak jeżeli spełnia to swoje zadanie w zadowalającym stopniu możesz to tak zostawić. |
|
|
![]()
Post
#7
|
|
![]() Grupa: Zarejestrowani Postów: 78 Pomógł: 9 Dołączył: 2.02.2011 Skąd: undefined Ostrzeżenie: (0%) ![]() ![]() |
No tak. Ale jeśli podczas dalszego parserowania wyrażenia znajdzie błąd (brak operatora itp.) to jestem w stanie znaleźć błąd występujący w składni.
Nawet jeśli miałbym coś innego użyć do tokenowania to co to miałoby być i jak miałbym tego użyć ![]() |
|
|
![]()
Post
#8
|
|
![]() Grupa: Zarejestrowani Postów: 6 476 Pomógł: 1306 Dołączył: 6.08.2006 Skąd: Kraków Ostrzeżenie: (0%) ![]() ![]() |
Kod 3 + #23 Przy pomocy wyrażeń otrzymasz 3, +, 23 podczas gdy powinieneś otrzymać błąd - wdarł się # przez który na dobrą sprawę nie wiadomo co tak na prawdę powinno się zrobić.Cytat Nawet jeśli miałbym coś innego użyć do tokenowania to co to miałoby być i jak miałbym tego użyć Odczytujesz znak po znaku otrzymany ciąg, wyciągając z niego kolejne elementy. W sieci jest od groma różnego rodzaju parserów (bibliotek) więc łatwo można podejrzeć w jaki sposób napisać całość (pewnie nawet gotowca można znaleźć).
|
|
|
![]()
Post
#9
|
|
![]() Grupa: Zarejestrowani Postów: 78 Pomógł: 9 Dołączył: 2.02.2011 Skąd: undefined Ostrzeżenie: (0%) ![]() ![]() |
czyli
Cytat Czyli analizuję ciąg od początku, sprawdzam co mam a potem czego oczekuję następnie. Jeśli tak nie jest to błąd a jeśli jest to następny znak? Bo jak narazie stworzyłem coś takiego:
i na większości wyrażeń działa (oczywiście jeszcze błędów nie obsługuje) |
|
|
![]()
Post
#10
|
|
![]() Grupa: Zarejestrowani Postów: 6 476 Pomógł: 1306 Dołączył: 6.08.2006 Skąd: Kraków Ostrzeżenie: (0%) ![]() ![]() |
Cytat Czyli analizuję ciąg od początku, sprawdzam co mam a potem czego oczekuję następnie. Jeśli tak nie jest to błąd a jeśli jest to następny znak? Mógłbyś już nawet na etapie wykrywania tokenów wywalać pewne błędy składni, np. ")" nie może się pojawić jeżeli nie ma poprzedzającego go "(", ale generalnie darowałbym sobie na tym etapie takie coś. Nie możesz przewidzieć wszystkich w tym miejscu, w dodatku "zabrudziłbyś" kod tokenizera.1. Odczytujesz wszystkie tokeny, nie bacząc na ich poprawność względem gramatyki języka (tutaj: wyrażeń matematycznych). 2. Na podstawie odczytanych tokenów próbujesz utworzyć AST. Na tym etapie wyłapujesz błędy składni. EDIT: Nie analizowałem całości ale kilka błędów już rzuciło mi się w oczy: 1. "-" (minus) to nie tylko operator odejmowania (operator dwuargumentowy), ale i operator wskazujący na wartość przeciwną (ujemną) następującego po nim wyrażenia (operator jednoargumentowy), np.: $a = -$b; 2. W przypadku gdy będziesz miał zapis 2 + invalidFunction(6) w chwili obecnej parser potraktuje invalidFunction jako... zmienną. Nie muszę chyba tłumaczyć, że jest to bzdura. EDIT: Na prawdę polecam poświęcić ze dwie godz nad wynikami wyszukiwania c / java / php math parser (język jest tutaj bez znaczenia) - jest tego sporo Ten post edytował Crozin 29.11.2011, 00:45:21 |
|
|
![]()
Post
#11
|
|
![]() Grupa: Zarejestrowani Postów: 78 Pomógł: 9 Dołączył: 2.02.2011 Skąd: undefined Ostrzeżenie: (0%) ![]() ![]() |
Co do minusa to podam RPN z przykładowych wyrażeń tworzy takie:
Kod -3*(-6) = 3 0 6 - * - ----------------- (-3)*(-6) = 0 3 - 0 6 - * ----------------- (2-3)*(1-6) = 2 3 - 1 6 - * ----------------- -1-1 = 1 - 1 - także myślę że z tym sobie poradzi. Postaram się poczytać o parserach coś więcej. |
|
|
![]() ![]() |
![]() |
Aktualny czas: 19.08.2025 - 21:17 |