Devilmarkus Geschrieben 9. März 2010 Geschrieben 9. März 2010 Hallo, ich habe folgendes Problem: Ich benötige für simple Rechenaufgaben einen MathParser. Bevor Ihr nun alle postet:"Google ist Dein Freund": Ich habe mir einen Wolf gegoogelt. Leider habe ich nur einen Ansatzweise geeigneten Parser gefunden und ihn für meine Bedürfnisse angepasst. (Ich möchte A: Keine Shareware verwenden und B: Keine neue Bibliothek einsetzen) Hier nun mein modifizierter Parser: /* * To change this template, choose Tools | Templates * and open the template in the editor. */ //package jemu.util.ass; //import jemu.core.Util; public class MathParser { private static final char[] validOperators = {'/', '*', '+', '-', '^'}; private MathParser() { } public static int evaluate(String leftSide, char oper, String rightSide) throws IllegalArgumentException { // leftSide = Z80Assembler.setLabels(leftSide); // leftSide = Z80Assembler.putEQU(leftSide); // rightSide = Z80Assembler.setLabels(rightSide); // rightSide = Z80Assembler.putEQU(rightSide); // leftSide.replace("#", "&"); // leftSide.replace("$", "&"); // rightSide.replace("#", "&"); // rightSide.replace("$", "&"); // if (leftSide.startsWith("&")) { // try { // leftSide = "" + Util.hexValue(leftSide.replace("&", "")); // } catch (Exception e) { // } // } // if (rightSide.startsWith("&")) { // try { // rightSide = "" + Util.hexValue(rightSide.replace("&", "")); // } catch (Exception e) { // } // } // System.out.println("Evaluating: " + leftSide + " (" + oper + ") " + rightSide); int total = 0; int leftResult = 0; int rightResult = 0; int operatorLoc = findLastOperatorLocation(leftSide); if (operatorLoc > 0 && operatorLoc < leftSide.length() - 1) { leftResult = evaluate(leftSide.substring(0, operatorLoc), leftSide.charAt(operatorLoc), leftSide.substring(operatorLoc + 1, leftSide.length())); } else { try { leftResult = Integer.parseInt(leftSide); } catch (Exception e) { throw new IllegalArgumentException("Invalid value found in portion of equation: " + leftSide); } } operatorLoc = findOperatorLocation(rightSide); if (operatorLoc > 0 && operatorLoc < rightSide.length() - 1) { rightResult = evaluate(rightSide.substring(0, operatorLoc), rightSide.charAt(operatorLoc), rightSide.substring(operatorLoc + 1, rightSide.length())); } else { try { rightResult = Integer.parseInt(rightSide); } catch (Exception e) { throw new IllegalArgumentException("Invalid value found in portion of equation: " + rightSide); } } // System.out.println("Getting result of: " + leftResult + " " + oper + " " + rightResult); switch (oper) { case '/': total = leftResult / rightResult; break; case '*': total = leftResult * rightResult; break; case '+': total = leftResult + rightResult; break; case '-': total = leftResult - rightResult; break; case '^': total = (int)Math.pow(leftResult, rightResult); break; default: throw new IllegalArgumentException("Unknown operator."); } // System.out.println("Returning a result of: " + total); return total; } private static int findOperatorLocation(String string) { int index = -1; for (int i = validOperators.length - 1; i >= 0; i--) { index = string.indexOf(validOperators[i]); if (index >= 0) { return index; } } return index; } private static int findLastOperatorLocation(String string) { int index = -1; for (int i = validOperators.length - 1; i >= 0; i--) { index = string.lastIndexOf(validOperators[i]); if (index >= 0) { return index; } } return index; } public static int processEquation(String equation) throws IllegalArgumentException { return evaluate("0", '+', equation); } public static String parseMath(String input) { try { int result = processEquation(input); return "" + result; } catch (IllegalArgumentException iae) { } return null; } public static int parseMaths(String input) { try { int result = processEquation(input); return result; } catch (IllegalArgumentException iae) { } return 0; } public static void main(String[] args) { String usage = "Usage: java MathParser equation\nWhere equation is a series" + " of integers separated by valid operators (+,-,/,*)"; if (args.length < 1 || args[0].length() == 0) { System.out.println(usage); String calc = "10+20-4"; int result = MathParser.processEquation(calc); System.out.println("The result of your equation (" + calc + ") should be: 26 is: " + result); calc = "20-10+4"; result = MathParser.processEquation(calc); System.out.println("The result of your equation (" + calc + ") should be: 14 is: " + result); calc = "20-2+4*4"; result = MathParser.processEquation(calc); System.out.println("The result of your equation (" + calc + ") should be: 34 is: " + result); calc = "3^13"; result = MathParser.processEquation(calc); System.out.println("The result of your equation (" + calc + ") should be: 1594323 is: " + result); } else { String equation = args[0]; try { int result = MathParser.processEquation(equation); System.out.println("The result of your equation (" + equation + ") is: " + result); } catch (IllegalArgumentException iae) { System.out.println(iae.getMessage() + "\n" + usage); } } } } Das Problem ist: Er soll einfach nur simpel rechnen, nichts aussergewöhnliches. (Klammern, andere Logarithmische Funktionen werden nicht benötigt) Nur ist das Resultat nicht immer wie erwünscht: The result of your equation (10+20-4) should be: 26 is: 26 The result of your equation (20-10+4) should be: 14 is: 6 The result of your equation (20-2+4*4) should be: 34 is: 2 The result of your equation (3^13) should be: 1594323 is: 1594323 Wenn jemand einen Plan hat, dieses hier zu ändern, wäre ich dafür sehr dankbar! Gruss, Markus P.s: Ich habe einige "Programmspezifischen" Sachen weg markiert, damit jeder diesen Code kompilieren kann. Zitieren
flashpixx Geschrieben 9. März 2010 Geschrieben 9. März 2010 Dein Code ist extrem schwer zu lesen. Gerade bei solchen Problemen arbeitet man mit einer Grammatik, die sicherstellt, dass der eingegebene Ausdruck überhaupt korrekt ist. Ich vermute mal, dass der Ausdruck nicht passend geparst wird Der Ansatz hier ist die Polnische Notation ? Wikipedia Die Gleichung wird als Baum dargestellt und zur Berechnung einfach nur traversiert, wobei an jedem Knoten die Berechnung stattfindet und in den Blättern die Werte stehen. Zitieren
Devilmarkus Geschrieben 9. März 2010 Autor Geschrieben 9. März 2010 Der Ansatz hier ist die Polnische Notation ? Wikipedia Die Gleichung wird als Baum dargestellt und zur Berechnung einfach nur traversiert, wobei an jedem Knoten die Berechnung stattfindet und in den Blättern die Werte stehen. In der Polnischen Notation werden die Operatoren VOR die Ziffern geschrieben, wie es aussieht. Ich benötige sie aber auch mittendrin... Ich weiss, dass der Code schwer zu verstehen/lesen ist. Mein Kopf raucht auch schon. Mir würde folgendes eventuell helfen: Beispiel: 10+20+3*5 Wenn man nun die 3*5 an den Anfang setzen würde (Also die letzte Berechnung) dann würde das gehen: 3*5+10+20 würde mir mein erwünschtes Ergebnis liefern. Irgendeine Idee, wie man also die letzte Operation abschneiden kann und der Aufgabe vorne an stellen kann? Begonnen habe ich: public static String parseMath(String input) { input = checkInput(input); try { int result = processEquation(input); return "" + result; } catch (IllegalArgumentException iae) { } return null; } public static String checkInput(String inp) { int ops = 0; char[] lastop = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; for (int i = 0; i < inp.length(); i++) { for (int p = 0; p < validOperators.length; p++) { if (inp.charAt(i) == (char)validOperators[p]){ ops++; lastop[ops] = inp.charAt(i); break; } } } String firstop = ""+lastop[ops-1]; if (ops > 1){ String newString =""; } System.out.println(ops +" operations. Last operation was:"+lastop[ops-1]); return inp; } Dieser Code soll also die letze Rechenoperation vorne anstellen... Nur wie? *Kopfrauchtwiedoof* Zitieren
flashpixx Geschrieben 9. März 2010 Geschrieben 9. März 2010 In der Polnischen Notation werden die Operatoren VOR die Ziffern geschrieben, wie es aussieht. Ich benötige sie aber auch mittendrin... Ich frage mich gerade, warum es überhaupt solche Notationen gibt? Ich empfehle einmal ganz dringend, dass Du Dir die Grundlagen der Informatik (Datenstrukturen und Algorithmen) anschaust, denn es ist schon sinnvoll bekannte Strukturen zu nutzen. Außerdem sehe ich überhaupt keinen fachlichen Grund die polnische Notation nicht einzusetzen. Zitieren
Devilmarkus Geschrieben 9. März 2010 Autor Geschrieben 9. März 2010 Habs (mit klammern, sinus, cosinus, sqr, exponent usw) SourceForge.net Repository - [javacpc] View of /JavaCPC/src/jemu/util/ass/Parser.java Zitieren
flashpixx Geschrieben 10. März 2010 Geschrieben 10. März 2010 was ist mit einem Konstrukt wie 2^sin(5^(3+5)) ? Zitieren
Devilmarkus Geschrieben 10. März 2010 Autor Geschrieben 10. März 2010 was ist mit einem Konstrukt wie 2^sin(5^(3+5)) ? Liefert er mir: 0.5895869474760311 Richtig? Zitieren
flashpixx Geschrieben 10. März 2010 Geschrieben 10. März 2010 Liefert er mir: 0.5895869474760311 Richtig? Nimm doch mal nen Taschenrechner und reche es selbst nach, wenn ich mich nicht vertippt habe, müsste dort ~1,34035788489 heraus kommen. Zitieren
LadyPreis Geschrieben 10. März 2010 Geschrieben 10. März 2010 Nimm doch mal nen Taschenrechner und reche es selbst nach, wenn ich mich nicht vertippt habe, müsste dort ~1,34035788489 heraus kommen. Hast dich nicht vertippt. Habe ebenfalls 1,3403578848884253087706899844829 raus Zitieren
Devilmarkus Geschrieben 10. März 2010 Autor Geschrieben 10. März 2010 Nimm doch mal nen Taschenrechner und reche es selbst nach, wenn ich mich nicht vertippt habe, müsste dort ~1,34035788489 heraus kommen. Ja das kommt heraus, wenn man mit Radiant rechnet. Kleine Änderung in meinem Parser: Input:2^sin(5^(3+5)) Result = 1.3403513252683301 if (fun.equals("sin")) { expression = transformMinus("(" + Math.sin(calculatePostFix(generatePostFix(expression))* 0.0174532925) + ")"); } else { if (fun.equals("cos")) { expression = transformMinus("(" + Math.cos(calculatePostFix(generatePostFix(expression))* 0.0174532925) + ")"); } else { if (fun.equals("tan")) { expression = transformMinus("(" + Math.tan(calculatePostFix(generatePostFix(expression))* 0.0174532925) + ")"); } } } * 0.0174532925 um in Radiant umzurechnen. Zitieren
LadyPreis Geschrieben 10. März 2010 Geschrieben 10. März 2010 bin jetzt nicht so in dem Themengebiet deines Projektes drin, aber ist es nicht sehr unschön, mit fixen Werten Umrechungen durchzuführen? ich gehe jetzt einfach mal davon aus, dass dein ergebnis eine geringe Abweichung gegenüber meinem hat, weil deine hard-codierte Zahl nicht so genau ist wie die, die mein windows calc benutzt Zitieren
Devilmarkus Geschrieben 10. März 2010 Autor Geschrieben 10. März 2010 bin jetzt nicht so in dem Themengebiet deines Projektes drin, aber ist es nicht sehr unschön, mit fixen Werten Umrechungen durchzuführen? ich gehe jetzt einfach mal davon aus, dass dein ergebnis eine geringe Abweichung gegenüber meinem hat, weil deine hard-codierte Zahl nicht so genau ist wie die, die mein windows calc benutzt Ich hab es geändert. Du hattest Recht! Der Wert war zu ungenau. Input:2^sin(5^(3+5)) Result = 1.3403578848874218 Sollte nun akkurat sein. if (fun.equals("sin")) { expression = transformMinus("(" + Math.sin(Math.toRadians(calculatePostFix(generatePostFix(expression)))) + ")"); } else { if (fun.equals("cos")) { expression = transformMinus("(" + Math.cos(Math.toRadians(calculatePostFix(generatePostFix(expression)))) + ")"); } else { if (fun.equals("tan")) { expression = transformMinus("(" + Math.tan(Math.toRadians(calculatePostFix(generatePostFix(expression)))) + ")"); } } } Zitieren
LadyPreis Geschrieben 10. März 2010 Geschrieben 10. März 2010 dann sind wir schonmal nen Schritt weiter. Nun zu deinen Ergebnissen von oben: The result of your equation (10+20-4) should be: 26 is: 26 scheint ja ok zu sein The result of your equation (3^13) should be: 1594323 is: 1594323 scheint auch ok zu sein The result of your equation (20-10+4) should be: 14 is: 6 hmmmm.... The result of your equation (20-2+4*4) should be: 34 is: 2 und nochmal hmmmm..... Sieht mir beiden so aus, als ob er die rechenzeichen nicht richtig auswertet. 1.: er rechnet 20-10-4 2.: er rechnet 20-(4*4 + 2) hab jetzt nicht die möglichkeit gehabt, mir deinen code genau anzuschauen. aber es sieht stark nach einem falschen evaluierung der zeichen bzw der reihenfolge aus Zitieren
Devilmarkus Geschrieben 10. März 2010 Autor Geschrieben 10. März 2010 dann sind wir schonmal nen Schritt weiter. Nun zu deinen Ergebnissen von oben: .... hab jetzt nicht die möglichkeit gehabt, mir deinen code genau anzuschauen. aber es sieht stark nach einem falschen evaluierung der zeichen bzw der reihenfolge aus Das Problem hat sich gelöst. Ich hatte vorher falsch geparst. Der jetzige Parser tut wie er soll. Ich kann sogar die Punkt-vor-Strich Rechnung abschalten. Das brauche ich für meinen eigentlichen Programmteil: Z80 Assembler. Der benötigt diesen Parser. Beispiel: DS &3318-&3301+4*14 DS bedeutet: er soll eine bestimmte Anzahl an (in diesem Fall) Nullen aneinanderreihen. Diese Rechnung ergibt nach "normaler" Regel 79! Allerdings soll der Assembler kompatibel zu einem 'echten' Z80 Assembler sein und deshalb keine Punkt-vor-Strich Rechnung machen, Also kommt als Ergebnis 378 heraus... (Macht einen gewaltigen Unterschied, ist aber in diesem Fall das gewünschte Ergebnis) Bild: Original Maxam-Assembler mit derselben Berechnung Zitieren
Devilmarkus Geschrieben 10. März 2010 Autor Geschrieben 10. März 2010 Hier nun der komplette Parser: View Source Download Source Für den normalen User bleibt nur die Labelgeschichte zu entfernen (Zeilen 258 / 259) Viel Spass damit. (Er kann nun auch mit 'pi' rechnen!) Zitieren
Devilmarkus Geschrieben 10. März 2010 Autor Geschrieben 10. März 2010 Zum "herumspielen" habe ich nun ein Applet gebastelt.... Scientific Calculator Applet Zitieren
UltimateRuppi Geschrieben 11. März 2010 Geschrieben 11. März 2010 Ist ja an sich eine feine Sache... Mir ist aufgefallen, dass 2^(3+1)*2 = Err, 2^(3+1)+2 = 18 2^(3+1)-2 = 14 2^(3+1)/2 = 8 2^2*2 = Err es scheint so als wäre da einProblem mit dem *-Operator nach einem Exponenten Zitieren
flashpixx Geschrieben 11. März 2010 Geschrieben 11. März 2010 es scheint so als wäre da einProblem mit dem *-Operator nach einem Exponenten Wie ich schon geschrieben hatte, wird das ein Problem des Parsings sein. Gerade für mathematische Ausdrücke wird ein Baum aufgebaut in dem man den Ausdruck ablegt. Aus der entsprechenden Notation, erst Operator, dann Werte, ist die Verarbeitung nach den mathematischen Gesetzen leicht möglich. Ist ein klassisches Beispiel der Informatik 1/2 Vorlesung. Zitieren
Devilmarkus Geschrieben 11. März 2010 Autor Geschrieben 11. März 2010 Ist ja an sich eine feine Sache... Mir ist aufgefallen, dass 2^(3+1)*2 = Err, 2^(3+1)+2 = 18 2^(3+1)-2 = 14 2^(3+1)/2 = 8 2^2*2 = Err es scheint so als wäre da einProblem mit dem *-Operator nach einem Exponenten Sollte nun alles funktionieren. Übrigens lustig: In meiner Benachrichtigungsmail wurde die letzte Aufgabe so abgeändert: 2²*2 = Err Auch dieser Fall sollte jetzt funktionieren. ² und ³ sollten korrekt gelesen werden. Zitieren
UltimateRuppi Geschrieben 11. März 2010 Geschrieben 11. März 2010 Wie ich schon geschrieben hatte, wird das ein Problem des Parsings sein. Gerade für mathematische Ausdrücke wird ein Baum aufgebaut in dem man den Ausdruck ablegt. Aus der entsprechenden Notation, erst Operator, dann Werte, ist die Verarbeitung nach den mathematischen Gesetzen leicht möglich. Ist ein klassisches Beispiel der Informatik 1/2 Vorlesung. Das ist mir durchaus bewusst ich wollte mit dem Satz den TE nur behilflich sein die Intention meiner Auflistung leichter zu verstehen und dementsprechend die Stelle des Fehlers im Quellcode leichter zu finden Zitieren
flashpixx Geschrieben 11. März 2010 Geschrieben 11. März 2010 Auch dieser Fall sollte jetzt funktionieren. ² und ³ sollten korrekt gelesen werden. Was ist mit ^0.5 ? bzw auch transzendeten Zahlen aus R z.B. ^-Pi Zitieren
Devilmarkus Geschrieben 11. März 2010 Autor Geschrieben 11. März 2010 (bearbeitet) Das ist mir durchaus bewusst ich wollte mit dem Satz den TE nur behilflich sein die Intention meiner Auflistung leichter zu verstehen und dementsprechend die Stelle des Fehlers im Quellcode leichter zu finden Danke, das ist auch hilfreicher für mich. Dinge wie "Aus Dokument soundso oder aus Regel soundso" nützen mir garnichts. - JavaDocs verstehe ich (noch) nicht. - Ich habe nie Java-Schulungen mitgemacht. - Alles, was ich in Java mache, habe ich mir selbst beigebracht (Trial & error) oder hier im Forum erfragt. Was ist mit ^0.5 ? bzw auch transzendeten Zahlen aus R z.B. ^-Pi 2^-pi ergibt: 0.11331473229676088 2^0.5 ergibt: 1.4142135623730951 ^0.5 ergibt: 0.5 (Ob es richtig ist, weiss ich nicht. Bin ein null-Mathematiker) Bearbeitet 11. März 2010 von Devilmarkus Zitieren
flashpixx Geschrieben 11. März 2010 Geschrieben 11. März 2010 Danke, das ist auch hilfreicher für mich. Dinge wie "Aus Dokument soundso oder aus Regel soundso" nützen mir garnichts. - JavaDocs verstehe ich (noch) nicht. - Ich habe nie Java-Schulungen mitgemacht. - Alles, was ich in Java mache, habe ich mir selbst beigebracht (Trial & error) oder hier im Forum erfragt. Du scheinst aber gerade in letzterem Punkt nicht immer die Ratschläge zu beherzigen, denn neben der Dokumentation, die es nicht nur Dir leichter macht Deinen Code zu verstehen, kann man auch als Außenstehender versuchen Fehler zu finden. Auch mein Hinweis zur Polnische Notation würde Dir einiges an Arbeit ersparen, vor allem weil im Wikipedia Artikel folgendes steht: Im Bereich der Mathematik werden in der üblichen Schreibweise viele Funktionen in polnischer Notation aufgeschrieben, z. B. Sinus („sin 30“) oder Logarithmus („ln 10“). Im Allgemeinen ist in der Mathematik aber eine Infix-Schreibweise üblich, bei der die Operatoren zwischen ihre Argumente geschrieben werden (z. B. „1 + 2“). Auch der Hinweis auf die Infixnotation wird dort gegeben (Ob es richtig ist, weiss ich nicht. Bin ein null-Mathematiker) Wenn Du einen Parser schreibst, der einen mathematischen Ausdruck verarbeitet, dann solltest Du auch validieren können, ob das Ergebnis richtig ist. Zitieren
Devilmarkus Geschrieben 11. März 2010 Autor Geschrieben 11. März 2010 Wenn Du einen Parser schreibst, der einen mathematischen Ausdruck verarbeitet, dann solltest Du auch validieren können, ob das Ergebnis richtig ist. Ich habe Teile eines vorhandenen Parsers verwendet und an meine Bedürfnisse angepasst / erweitert. Für das Wesentliche, was er können muss, nämlich genauso rechnen, wie der MAXAM-Z80 Assembler das kann, reicht er völlig aus... Der "Scientific Calculator" ist eigentlich nur ein Überbleibsel einer simplen Eingabe-GUI womit ich den Parser für meine Bedürfnisse getestet habe. (Ich war nur zu Faul, den wieder zu löschen) Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.