Ich versuche, eine Java-Routine zu schreiben, die mathematische Ausdrücke aus String
Werte wie:
"5+3"
"10-40"
"(1+10)*3"
Ich möchte eine Vielzahl von if-then-else-Anweisungen vermeiden. Wie kann ich das tun?
Ich versuche, eine Java-Routine zu schreiben, die mathematische Ausdrücke aus String
Werte wie:
"5+3"
"10-40"
"(1+10)*3"
Ich möchte eine Vielzahl von if-then-else-Anweisungen vermeiden. Wie kann ich das tun?
Mit JDK1.6 können Sie die eingebaute Javascript-Engine verwenden.
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Test {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+2";
System.out.println(engine.eval(foo));
}
}
Hier scheint es ein großes Problem zu geben: Es wird ein Skript ausgeführt, nicht ein Ausdruck ausgewertet. Um das klarzustellen, engine.eval("8;40+2"), gibt 42 ! Wenn Sie einen Ausdrucks-Parser wollen, der auch die Syntax überprüft, habe ich gerade einen fertiggestellt (weil ich nichts gefunden habe, was meinen Bedürfnissen entspricht): Javaluator .
Nebenbei bemerkt, wenn Sie das Ergebnis dieses Ausdrucks an anderer Stelle in Ihrem Code verwenden müssen, können Sie das Ergebnis wie folgt in einen Double-Wert umwandeln: return (Double) engine.eval(foo);
Sicherheitshinweis: Sie sollten dies niemals in einem Serverkontext mit Benutzereingaben verwenden. Das ausgeführte JavaScript kann auf alle Java-Klassen zugreifen und so Ihre Anwendung unbegrenzt kapern.
Ich habe dies geschrieben eval
Methode für arithmetische Ausdrücke, um diese Frage zu beantworten. Es kann Addition, Subtraktion, Multiplikation, Division, Potenzierung (unter Verwendung der ^
Symbol) und ein paar grundlegende Funktionen wie sqrt
. Es unterstützt die Gruppierung mit (
... )
und es erhält den Operator Vorrang y Assoziativität Regeln korrekt.
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)` | number
// | functionName `(` expression `)` | functionName factor
// | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else return x;
}
}
double parseFactor() {
if (eat('+')) return +parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')'");
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
if (eat('(')) {
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')' after argument to " + func);
} else {
x = parseFactor();
}
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
return x;
}
}.parse();
}
Beispiel:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Ausgabe: 7.5 (was richtig ist)
Der Parser ist ein Parser mit rekursivem Abstieg und verwendet daher intern separate Parse-Methoden für jede Stufe der Operatorpriorität in seiner Grammatik. Ich habe es absichtlich kurz aber hier sind einige Ideen, die Sie vielleicht erweitern möchten:
Variablen:
Der Teil des Parsers, der die Namen für Funktionen liest, kann leicht geändert werden, um auch benutzerdefinierte Variablen zu behandeln, indem man die Namen in einer Variablentabelle nachschlägt, die an die eval
Methode, wie zum Beispiel eine Map<String,Double> variables
.
Getrennte Zusammenstellung und Auswertung:
Was wäre, wenn Sie, nachdem Sie die Unterstützung für Variablen hinzugefügt haben, denselben Ausdruck Millionen von Malen mit geänderten Variablen auswerten wollten, ohne ihn jedes Mal zu parsen? Das ist möglich. Definieren Sie zunächst eine Schnittstelle für die Auswertung des vorkompilierten Ausdrucks:
@FunctionalInterface
interface Expression {
double eval();
}
Um nun die ursprüngliche "eval"-Funktion in eine "parse"-Funktion umzuwandeln, ändern Sie alle Methoden, die Folgendes zurückgeben double
s, so dass sie stattdessen eine Instanz dieser Schnittstelle zurückgeben. Die Lambda-Syntax von Java 8 eignet sich gut für diese Aufgabe. Beispiel für eine der geänderten Methoden:
Expression parseExpression() {
Expression x = parseTerm();
for (;;) {
if (eat('+')) { // addition
Expression a = x, b = parseTerm();
x = (() -> a.eval() + b.eval());
} else if (eat('-')) { // subtraction
Expression a = x, b = parseTerm();
x = (() -> a.eval() - b.eval());
} else {
return x;
}
}
}
Das erzeugt einen rekursiven Baum von Expression
Objekte, die den kompilierten Ausdruck darstellen (ein abstrakter Syntaxbaum ). Dann können Sie es einmal kompilieren und wiederholt mit verschiedenen Werten auswerten:
public static void main(String[] args) {
Map<String,Double> variables = new HashMap<>();
Expression exp = parse("x^2 - x + 2", variables);
for (double x = -20; x <= +20; x++) {
variables.put("x", x);
System.out.println(x + " => " + exp.eval());
}
}
Verschiedene Datentypen:
Anstelle von double
könnten Sie den Evaluator ändern, um etwas Leistungsfähigeres zu verwenden wie BigDecimal
oder eine Klasse, die komplexe Zahlen oder rationale Zahlen (Brüche) implementiert. Sie könnten sogar verwenden Object
und erlaubt eine Mischung von Datentypen in Ausdrücken, genau wie eine echte Programmiersprache :)
Alle Codes in dieser Antwort freigegeben <a href="https://creativecommons.org/publicdomain/zero/1.0/" rel="noreferrer">zum öffentlichen Bereich </a>. Viel Spaß!
Netter Algorithmus, von dem aus ich es geschafft habe, logische Operatoren zu implementieren. Wir haben separate Klassen für Funktionen erstellt, um eine Funktion zu evaluieren, also wie deine Idee von Variablen, erstelle ich eine Karte mit Funktionen und suche nach dem Funktionsnamen. Jede Funktion implementiert eine Schnittstelle mit einer Methode eval (T rightOperator , T leftOperator), so dass wir jederzeit Funktionen hinzufügen können, ohne den Code des Algorithmus zu ändern. Und es ist eine gute Idee, dass es mit generischen Typen funktioniert. Vielen Dank an Sie!
Ich versuche, eine Beschreibung dessen zu geben, was ich aus dem von Boann geschriebenen Code und den im Wiki beschriebenen Beispielen verstehe.Die Logik dieses Algorithmus beginnt mit den Regeln der Operationsreihenfolge. 1. Operatorzeichen | Variablenbewertung | Funktionsaufruf | Klammern (Unterausdrücke); 2. Potenzierung; 3;
Für mein Universitätsprojekt war ich auf der Suche nach einem Parser / Evaluator, der sowohl einfache Formeln als auch kompliziertere Gleichungen (insbesondere iterierte Operatoren) unterstützt. Ich fand eine sehr schöne Open-Source-Bibliothek für JAVA und .NET namens mXparser. Ich werde ein paar Beispiele geben, um ein Gefühl für die Syntax zu bekommen. Für weitere Anweisungen besuchen Sie bitte die Projekt-Website (insbesondere den Tutorial-Bereich).
https://mathparser.org/mxparser-tutorial/
Und einige Beispiele
1 - Einfache Furmula
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - Benutzerdefinierte Argumente und Konstanten
Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()
3 - Benutzerdefinierte Funktionen
Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()
4 - Iteration
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Kürzlich gefunden - falls Sie die Syntax ausprobieren möchten (und den erweiterten Anwendungsfall sehen wollen), können Sie die Skalar Taschenrechner app die von mXparser unterstützt wird.
Bisher ist dies die beste mathematische Bibliothek, die es gibt; einfach zu starten, einfach zu benutzen und erweiterbar. Sollte auf jeden Fall die beste Antwort sein.
Ich habe festgestellt, dass mXparser illegale Formeln nicht erkennen kann, z. B. wird '0/0' als '0' ausgegeben. Wie kann ich dieses Problem lösen?
HIER ist eine weitere Open-Source-Bibliothek auf GitHub namens EvalEx.
Im Gegensatz zur JavaScript-Engine ist diese Bibliothek ausschließlich auf die Auswertung mathematischer Ausdrücke ausgerichtet. Außerdem ist die Bibliothek erweiterbar und unterstützt die Verwendung von booleschen Operatoren und Klammern.
Dies ist in Ordnung, schlägt aber fehl, wenn wir versuchen, Werte von Vielfachen von 5 oder 10 zu multiplizieren, z.B. 65 * 6 ergibt 3.9E+2 ...
Aber es gibt eine Möglichkeit, dies zu beheben, indem man es in int umwandelt, d.h. int output = (int) 65*6 ergibt jetzt 390
Zur Klarstellung: Das ist kein Problem der Bibliothek, sondern ein Problem mit der Darstellung von Zahlen als Fließkommazahlen.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.