Ich versuche, eine Java-Routine zu schreiben, um einfache mathematische Ausdrücke anhand von String
Werten wie:
"5+3"
"10-40"
"10*3"
Ich möchte viele Wenn-Dann-Sonst-Aussagen vermeiden. Wie kann ich das machen?
Ich versuche, eine Java-Routine zu schreiben, um einfache mathematische Ausdrücke anhand von String
Werten wie:
"5+3"
"10-40"
"10*3"
Ich möchte viele Wenn-Dann-Sonst-Aussagen vermeiden. Wie kann ich das machen?
Antworten:
Mit JDK1.6 können Sie die integrierte 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));
}
}
return (Double) engine.eval(foo);
new javax.script.ScriptEngineManager().getEngineByName("JavaScript") .eval("var f = new java.io.FileWriter('hello.txt'); f.write('UNLIMITED POWER!'); f.close();");
- schreibt eine Datei über JavaScript (standardmäßig) in das aktuelle Verzeichnis des Programms
Ich habe diese eval
Methode für arithmetische Ausdrücke geschrieben, um diese Frage zu beantworten. Es führt Addition, Subtraktion, Multiplikation, Division, Exponentiation (unter Verwendung des ^
Symbols) und einige grundlegende Funktionen wie sqrt
. Es unterstützt die Gruppierung mit (
... )
und stellt die korrekten Vorrang- und Assoziativitätsregeln für Operatoren sicher .
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 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();
eat(')');
} 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);
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 rekursiver Abstiegsparser , verwendet also intern separate Analysemethoden für jede Ebene der Operatorrangfolge in seiner Grammatik. Ich habe es kurz gehalten, damit es leicht zu ändern ist, aber hier sind einige Ideen, mit denen Sie es möglicherweise erweitern möchten:
Variablen:
Das Bit des Parsers, das die Namen für Funktionen liest, kann leicht geändert werden, um auch benutzerdefinierte Variablen zu verarbeiten, indem Namen in einer an die eval
Methode übergebenen Variablentabelle nachgeschlagen werden, z Map<String,Double> variables
.
Separate Zusammenstellung und Auswertung:
Was wäre, wenn Sie, nachdem Sie die Unterstützung für Variablen hinzugefügt haben, denselben Ausdruck millionenfach mit geänderten Variablen auswerten möchten, ohne ihn jedes Mal zu analysieren? Es ist möglich. Definieren Sie zunächst eine Schnittstelle, die zum Auswerten des vorkompilierten Ausdrucks verwendet werden soll:
@FunctionalInterface
interface Expression {
double eval();
}
Ändern Sie nun alle Methoden, die double
s zurückgeben, und geben Sie stattdessen eine Instanz dieser Schnittstelle zurück. Die Lambda-Syntax von Java 8 eignet sich hervorragend dafür. Beispiel einer 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;
}
}
}
Dadurch wird ein rekursiver Baum von Expression
Objekten erstellt, 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:
Stattdessen double
können Sie den Evaluator so ändern, dass er eine leistungsfähigere BigDecimal
Klasse oder eine Klasse verwendet, die komplexe Zahlen oder rationale Zahlen (Brüche) implementiert. Sie können sogar Object
eine Mischung von Datentypen in Ausdrücken verwenden, genau wie eine echte Programmiersprache. :) :)
Der gesamte Code in dieser Antwort ist gemeinfrei . Habe Spaß!
double x = parseTerm();
den linken Operator for (;;) {...}
auswertet, danach sukzessive Operationen der tatsächlichen Auftragsebene (Addition, Subtraktion). Die gleiche Logik sind und in parseTerm-Methode. Der parseFactor hat keine nächste Ebene, daher gibt es nur Auswertungen von Methoden / Variablen oder im Falle einer Paranthesis - Auswertung des Unterausdrucks. Die boolean eat(int charToEat)
Methode prüft die Gleichheit des aktuellen Cursorzeichens mit dem Zeichen charToEat. Wenn gleich true zurückgibt und der Cursor zum nächsten Zeichen bewegt wird, verwende ich den Namen 'accept' dafür.
Für mein Universitätsprojekt suchte ich einen Parser / Evaluator, der sowohl Grundformeln als auch kompliziertere Gleichungen (insbesondere iterierte Operatoren) unterstützt. Ich habe eine sehr schöne Open Source Bibliothek für JAVA und .NET namens mXparser gefunden. Ich werde einige Beispiele geben, um ein Gefühl für die Syntax zu bekommen. Weitere Anweisungen finden Sie auf der Projektwebsite (insbesondere im 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 möchten), können Sie die Scalar Calculator- App herunterladen , die von mXparser unterstützt wird.
Freundliche Grüße
HIER ist eine weitere Open Source Bibliothek auf GitHub namens EvalEx.
Im Gegensatz zur JavaScript-Engine konzentriert sich diese Bibliothek nur auf die Auswertung mathematischer Ausdrücke. Darüber hinaus ist die Bibliothek erweiterbar und unterstützt die Verwendung von Booleschen Operatoren sowie Klammern.
Sie können auch den BeanShell- Interpreter ausprobieren :
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
Sie können Ausdrücke einfach auswerten, wenn Ihre Java-Anwendung bereits auf eine Datenbank zugreift, ohne andere JARs zu verwenden.
Bei einigen Datenbanken müssen Sie eine Dummy-Tabelle verwenden (z. B. die "duale" Tabelle von Oracle), bei anderen können Sie Ausdrücke auswerten, ohne eine Tabelle "auswählen" zu müssen.
Zum Beispiel in SQL Server oder SQLite
select (((12.10 +12.0))/ 233.0) amount
und in Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
Der Vorteil der Verwendung einer Datenbank besteht darin, dass Sie viele Ausdrücke gleichzeitig auswerten können. Außerdem können Sie in den meisten DBs hochkomplexe Ausdrücke verwenden und verfügen über eine Reihe zusätzlicher Funktionen, die bei Bedarf aufgerufen werden können.
Die Leistung kann jedoch beeinträchtigt werden, wenn viele einzelne Ausdrücke einzeln ausgewertet werden müssen, insbesondere wenn sich die Datenbank auf einem Netzwerkserver befindet.
Im Folgenden wird das Leistungsproblem in gewissem Umfang mithilfe einer speicherinternen Sqlite-Datenbank behoben.
Hier ist ein voll funktionsfähiges Beispiel in Java
Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();
Natürlich können Sie den obigen Code erweitern, um mehrere Berechnungen gleichzeitig durchzuführen.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
Dieser Artikel beschreibt verschiedene Ansätze. Hier sind die 2 wichtigsten Ansätze, die im Artikel erwähnt werden:
Ermöglicht Skripte, die Verweise auf Java-Objekte enthalten.
// Create or retrieve a JexlEngine
JexlEngine jexl = new JexlEngine();
// Create an expression object
String jexlExp = "foo.innerFoo.bar()";
Expression e = jexl.createExpression( jexlExp );
// Create a context and add data
JexlContext jctx = new MapContext();
jctx.set("foo", new Foo() );
// Now evaluate the expression, getting the result
Object o = e.evaluate(jctx);
private static void jsEvalWithVariable()
{
List<String> namesList = new ArrayList<String>();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
jsEngine.put("namesListKey", namesList);
System.out.println("Executing in script environment...");
try
{
jsEngine.eval("var x;" +
"var names = namesListKey.toArray();" +
"for(x in names) {" +
" println(names[x]);" +
"}" +
"namesListKey.add(\"Dana\");");
}
catch (ScriptException ex)
{
ex.printStackTrace();
}
}
Eine andere Möglichkeit ist die Verwendung von Spring Expression Language oder SpEL, die neben der Bewertung mathematischer Ausdrücke noch viel mehr bewirkt und daher möglicherweise etwas übertrieben ist. Sie müssen das Spring-Framework nicht verwenden, um diese Ausdrucksbibliothek zu verwenden, da sie eigenständig ist. Kopieren von Beispielen aus der SpEL-Dokumentation:
ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0
Weitere prägnante SpEL-Beispiele finden Sie hier und die vollständigen Dokumente hier
Wenn wir es implementieren wollen, können wir den folgenden Algorithmus verwenden: -
Während es noch Token zum Einlesen gibt,
1.1 Holen Sie sich das nächste Token. 1.2 Wenn das Token lautet:
1.2.1 Eine Zahl: Schieben Sie sie auf den Wertestapel.
1.2.2 Eine Variable: Holen Sie sich ihren Wert und drücken Sie auf den Wertestapel.
1.2.3 Eine linke Klammer: Schieben Sie sie auf den Bedienerstapel.
1.2.4 Eine rechte Klammer:
1 While the thing on top of the operator stack is not a
left parenthesis,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Pop the left parenthesis from the operator stack, and discard it.
1.2.5 Ein Operator (nennen Sie es thisOp):
1 While the operator stack is not empty, and the top thing on the
operator stack has the same or greater precedence as thisOp,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Push thisOp onto the operator stack.
Während der Operatorstapel nicht leer ist, 1 Pop den Operator aus dem Operatorstapel. 2 Pop den Wertestapel zweimal, um zwei Operanden zu erhalten. 3 Wenden Sie den Operator in der richtigen Reihenfolge auf die Operanden an. 4 Schieben Sie das Ergebnis auf den Wertestapel.
Zu diesem Zeitpunkt sollte der Operatorstapel leer sein und der Wertestapel sollte nur einen Wert enthalten, was das Endergebnis ist.
Dies ist eine weitere interessante Alternative https://github.com/Shy-Ta/expression-evaluator-demo
Die Verwendung ist sehr einfach und erledigt die Arbeit zum Beispiel:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Ich denke, wie auch immer Sie dies tun, es wird eine Menge bedingter Aussagen beinhalten. Für einzelne Operationen wie in Ihren Beispielen können Sie sie jedoch auf 4 if-Anweisungen mit so etwas wie beschränken
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
Es wird viel komplizierter, wenn Sie mehrere Operationen wie "4 + 5 * 6" ausführen möchten.
Wenn Sie versuchen, einen Taschenrechner zu erstellen, würde ich jeden Abschnitt der Berechnung separat (jede Zahl oder jeden Operator) und nicht als einzelne Zeichenfolge übergeben.
Es ist zu spät, um zu antworten, aber ich bin auf die gleiche Situation gestoßen, um den Ausdruck in Java zu bewerten. Es könnte jemandem helfen
MVEL
Wenn zur Laufzeit eine Auswertung von Ausdrücken durchgeführt wird, können wir einen Java-Code schreiben String
, um ihn darin auszuwerten.
String expressionStr = "x+y";
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("x", 10);
vars.put("y", 20);
ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
Object result = MVEL.executeExpression(statement, vars);
Vielleicht sehen Sie sich das Symja-Framework an :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Beachten Sie, dass definitiv komplexere Ausdrücke ausgewertet werden können:
// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2
Probieren Sie den folgenden Beispielcode mit der Javascript-Engine von JDK1.6 mit Code-Injection-Behandlung aus.
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
try {
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Object eval(String input) throws Exception{
try {
if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
throw new Exception("Invalid expression : " + input );
}
return engine.eval(input);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
Dies ergänzt tatsächlich die Antwort von @Boann. Es hat einen kleinen Fehler, der dazu führt, dass "-2 ^ 2" ein fehlerhaftes Ergebnis von -4,0 ergibt. Das Problem dafür ist der Punkt, an dem die Potenzierung in seinem bewertet wird. Verschieben Sie einfach die Potenzierung in den Block von parseTerm (), und alles wird gut. Schauen Sie sich das Folgende an, die Antwort von @ Boann ist leicht modifiziert. Änderung ist in den Kommentaren.
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 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 if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
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();
eat(')');
} 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);
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 -> This is causing a bit of problem
return x;
}
}.parse();
}
-2^2 = -4
ist eigentlich normal und kein Bug. Es wird gruppiert wie -(2^2)
. Probieren Sie es zum Beispiel auf Desmos aus. Ihr Code führt tatsächlich mehrere Fehler ein. Das erste ist, dass ^
nicht mehr von rechts nach links gruppiert wird. Mit anderen Worten, 2^3^2
soll gruppieren wie, 2^(3^2)
weil ^
es rechtsassoziativ ist, aber Ihre Modifikationen machen es gruppieren wie (2^3)^2
. Das zweite ist, dass ^
es eine höhere Priorität als *
und haben soll /
, aber Ihre Modifikationen behandeln es gleich. Siehe ideone.com/iN2mMa .
package ExpressionCalculator.expressioncalculator;
import java.text.DecimalFormat;
import java.util.Scanner;
public class ExpressionCalculator {
private static String addSpaces(String exp){
//Add space padding to operands.
//https://regex101.com/r/sJ9gM7/73
exp = exp.replaceAll("(?<=[0-9()])[\\/]", " / ");
exp = exp.replaceAll("(?<=[0-9()])[\\^]", " ^ ");
exp = exp.replaceAll("(?<=[0-9()])[\\*]", " * ");
exp = exp.replaceAll("(?<=[0-9()])[+]", " + ");
exp = exp.replaceAll("(?<=[0-9()])[-]", " - ");
//Keep replacing double spaces with single spaces until your string is properly formatted
/*while(exp.indexOf(" ") != -1){
exp = exp.replace(" ", " ");
}*/
exp = exp.replaceAll(" {2,}", " ");
return exp;
}
public static Double evaluate(String expr){
DecimalFormat df = new DecimalFormat("#.####");
//Format the expression properly before performing operations
String expression = addSpaces(expr);
try {
//We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and
//subtraction will be processed in following order
int indexClose = expression.indexOf(")");
int indexOpen = -1;
if (indexClose != -1) {
String substring = expression.substring(0, indexClose);
indexOpen = substring.lastIndexOf("(");
substring = substring.substring(indexOpen + 1).trim();
if(indexOpen != -1 && indexClose != -1) {
Double result = evaluate(substring);
expression = expression.substring(0, indexOpen).trim() + " " + result + " " + expression.substring(indexClose + 1).trim();
return evaluate(expression.trim());
}
}
String operation = "";
if(expression.indexOf(" / ") != -1){
operation = "/";
}else if(expression.indexOf(" ^ ") != -1){
operation = "^";
} else if(expression.indexOf(" * ") != -1){
operation = "*";
} else if(expression.indexOf(" + ") != -1){
operation = "+";
} else if(expression.indexOf(" - ") != -1){ //Avoid negative numbers
operation = "-";
} else{
return Double.parseDouble(expression);
}
int index = expression.indexOf(operation);
if(index != -1){
indexOpen = expression.lastIndexOf(" ", index - 2);
indexOpen = (indexOpen == -1)?0:indexOpen;
indexClose = expression.indexOf(" ", index + 2);
indexClose = (indexClose == -1)?expression.length():indexClose;
if(indexOpen != -1 && indexClose != -1) {
Double lhs = Double.parseDouble(expression.substring(indexOpen, index));
Double rhs = Double.parseDouble(expression.substring(index + 2, indexClose));
Double result = null;
switch (operation){
case "/":
//Prevent divide by 0 exception.
if(rhs == 0){
return null;
}
result = lhs / rhs;
break;
case "^":
result = Math.pow(lhs, rhs);
break;
case "*":
result = lhs * rhs;
break;
case "-":
result = lhs - rhs;
break;
case "+":
result = lhs + rhs;
break;
default:
break;
}
if(indexClose == expression.length()){
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose);
}else{
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose + 1);
}
return Double.valueOf(df.format(evaluate(expression.trim())));
}
}
}catch(Exception exp){
exp.printStackTrace();
}
return 0.0;
}
public static void main(String args[]){
Scanner scanner = new Scanner(System.in);
System.out.print("Enter an Mathematical Expression to Evaluate: ");
String input = scanner.nextLine();
System.out.println(evaluate(input));
}
}}
Wie wäre es mit so etwas:
String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
if(st.charAt(i)=='+')
{
result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
System.out.print(result);
}
}
und machen Sie das Gleiche für jeden anderen mathematischen Operator entsprechend.
Mit dem Shunt-Yard-Algorithmus von Djikstra ist es möglich, eine beliebige Ausdruckszeichenfolge in Infix-Notation in eine Postfix-Notation umzuwandeln . Das Ergebnis des Algorithmus kann dann als Eingabe für den Postfix-Algorithmus dienen, wobei das Ergebnis des Ausdrucks zurückgegeben wird.
Ich habe hier einen Artikel darüber geschrieben , mit einer Implementierung in Java
Noch eine Option: https://github.com/stefanhaustein/expressionparser
Ich habe dies implementiert, um eine einfache, aber flexible Option zu haben, die beides ermöglicht:
Der oben verlinkte TreeBuilder ist Teil eines CAS- Demopakets , das eine symbolische Ableitung durchführt. Es gibt auch ein BASIC-Interpreter- Beispiel, und ich habe damit begonnen, einen TypeScript-Interpreter damit zu erstellen .
Eine Java-Klasse, die mathematische Ausdrücke auswerten kann:
package test;
public class Calculator {
public static Double calculate(String expression){
if (expression == null || expression.length() == 0) {
return null;
}
return calc(expression.replace(" ", ""));
}
public static Double calc(String expression) {
if (expression.startsWith("(") && expression.endsWith(")")) {
return calc(expression.substring(1, expression.length() - 1));
}
String[] containerArr = new String[]{expression};
double leftVal = getNextOperand(containerArr);
expression = containerArr[0];
if (expression.length() == 0) {
return leftVal;
}
char operator = expression.charAt(0);
expression = expression.substring(1);
while (operator == '*' || operator == '/') {
containerArr[0] = expression;
double rightVal = getNextOperand(containerArr);
expression = containerArr[0];
if (operator == '*') {
leftVal = leftVal * rightVal;
} else {
leftVal = leftVal / rightVal;
}
if (expression.length() > 0) {
operator = expression.charAt(0);
expression = expression.substring(1);
} else {
return leftVal;
}
}
if (operator == '+') {
return leftVal + calc(expression);
} else {
return leftVal - calc(expression);
}
}
private static double getNextOperand(String[] exp){
double res;
if (exp[0].startsWith("(")) {
int open = 1;
int i = 1;
while (open != 0) {
if (exp[0].charAt(i) == '(') {
open++;
} else if (exp[0].charAt(i) == ')') {
open--;
}
i++;
}
res = calc(exp[0].substring(1, i - 1));
exp[0] = exp[0].substring(i);
} else {
int i = 1;
if (exp[0].charAt(0) == '-') {
i++;
}
while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
i++;
}
res = Double.parseDouble(exp[0].substring(0, i));
exp[0] = exp[0].substring(i);
}
return res;
}
private static boolean isNumber(int c) {
int zero = (int) '0';
int nine = (int) '9';
return (c >= zero && c <= nine) || c =='.';
}
public static void main(String[] args) {
System.out.println(calculate("(((( -6 )))) * 9 * -1"));
System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));
}
}
Externe Bibliotheken wie RHINO oder NASHORN können zum Ausführen von Javascript verwendet werden. Und Javascript kann einfache Formeln auswerten, ohne die Zeichenfolge zu analysieren. Keine Auswirkungen auf die Leistung, wenn der Code gut geschrieben ist. Unten sehen Sie ein Beispiel mit RHINO -
public class RhinoApp {
private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";
public void runJavaScript() {
Context jsCx = Context.enter();
Context.getCurrentContext().setOptimizationLevel(-1);
ScriptableObject scope = jsCx.initStandardObjects();
Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
Context.exit();
System.out.println(result);
}
import java.util.*;
public class check {
int ans;
String str="7 + 5";
StringTokenizer st=new StringTokenizer(str);
int v1=Integer.parseInt(st.nextToken());
String op=st.nextToken();
int v2=Integer.parseInt(st.nextToken());
if(op.equals("+")) { ans= v1 + v2; }
if(op.equals("-")) { ans= v1 - v2; }
//.........
}