una simplificar programa parser operaciones leer expresiones expresion evaluar evaluacion ecuaciones ecuacion como binarios aritmeticas arboles algebraicas java string math

java - simplificar - Evaluación de una expresión matemática dada en forma de cadena



programa en java de operaciones aritmeticas (24)

Estoy intentando escribir una rutina Java para evaluar expresiones matemáticas simples de valores de String como:

  1. "5+3"
  2. "10-40"
  3. "10*3"

Quiero evitar un montón de afirmaciones if-then-else. ¿Cómo puedo hacer esto?


Con JDK1.6, puede utilizar el motor Javascript incorporado.

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)); } }


Creo que de cualquier forma en la que hagas esto implicará muchas declaraciones condicionales. Pero para operaciones individuales como en sus ejemplos, podría limitarlo a 4 si las declaraciones con algo como

String math = "1+4"; if (math.split("+").length == 2) { //do calculation } else if (math.split("-").length == 2) { //do calculation } ...

Se vuelve mucho más complicado cuando se trata de operaciones múltiples como "4 + 5 * 6".

Si está tratando de construir una calculadora, entonces me atrevería a pasar cada sección del cálculo por separado (cada número u operador) en lugar de una sola cadena.


Es demasiado tarde para responder, pero me encontré con la misma situación para evaluar la expresión en java, podría ayudar a alguien

MVEL realiza la evaluación de las expresiones en tiempo de ejecución, podemos escribir un código java en String para evaluarlo en esto.

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);



Esta es otra alternativa interesante https://github.com/Shy-Ta/expression-evaluator-demo

El uso es muy simple y hace el trabajo, por ejemplo:

ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2"); assertEquals(BigDecimal.valueOf(11), evalExpr.eval());


Esto es en realidad complementando la respuesta dada por @Boann. Tiene un pequeño error que hace que "-2 ^ 2" dé un resultado erróneo de -4.0. El problema para eso es el punto en el cual se evalúa la exponenciación en el suyo. Simplemente mueva la exponenciación al bloque de parseTerm (), y todo estará bien. Eche un vistazo a lo siguiente, que es la respuesta de @ Boann ligeramente modificada. La modificación está en los comentarios.

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; } } }


He escrito este método de eval para expresiones aritméticas para responder a esta pregunta. Hace suma, resta, multiplicación, división, exponenciación (usando el símbolo ^ ) y algunas funciones básicas como sqrt . Es compatible con la agrupación utilizando ( ... ) y obtiene las reglas de associativity y precedence del operador correctas.

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(); }

Ejemplo:

System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));

Salida: 7.5 (que es correcta)

El analizador es un analizador de descenso recursivo , por lo que internamente utiliza métodos de análisis separados para cada nivel de precedencia del operador en su gramática. Lo mantuve corto, así que es fácil de modificar, pero aquí hay algunas ideas con las que querría expandirlo:

  • Variables:

    El bit del analizador que lee los nombres de las funciones también se puede cambiar fácilmente para manejar variables personalizadas, buscando nombres en una tabla de variables que se pasa al método eval , como las Map<String,Double> variables .

  • Recopilación y evaluación por separado:

    ¿Qué pasaría si, al agregar soporte para variables, quisiera evaluar la misma expresión millones de veces con variables cambiadas, sin analizarla cada vez? Es posible. Primero defina una interfaz para usar para evaluar la expresión precompilada:

    @FunctionalInterface interface Expression { double eval(); }

    Ahora cambie todos los métodos que devuelven el double s, por lo que en cambio devuelven una instancia de esa interfaz. La sintaxis lambda de Java 8 funciona muy bien para esto. Ejemplo de uno de los métodos modificados:

    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; } } }

    Eso construye un árbol recursivo de objetos de Expression que representan la expresión compilada (un árbol de sintaxis abstracta ). Luego puedes compilarlo una vez y evaluarlo repetidamente con diferentes valores:

    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()); } }

  • Diferentes tipos de datos:

    En lugar de double , podría cambiar el evaluador para usar algo más poderoso como BigDecimal , o una clase que implemente números complejos o números racionales (fracciones). Incluso podrías usar Object , permitiendo una mezcla de tipos de datos en expresiones, como un lenguaje de programación real. :)

Todo el código en esta respuesta fue lanzado al dominio público . ¡Que te diviertas!


La forma correcta de resolver esto es con un lexer y un parser . Puede escribir versiones sencillas de estos, o esas páginas también tienen enlaces a analizadores y analizadores Java.

Crear un analizador de descenso recursivo es un ejercicio de aprendizaje realmente bueno.


Otra forma es usar Spring Expression Language o SpEL, que hace mucho más junto con la evaluación de expresiones matemáticas, por lo tanto, tal vez un poco excesivo. No es necesario que esté utilizando Spring Framework para usar esta biblioteca de expresiones ya que es independiente. Copiando ejemplos de la documentación de SpEL:

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

Lea más ejemplos concisos de SpEL here y los documentos completos here


Otra opción más: https://github.com/stefanhaustein/expressionparser

He implementado esto para tener una opción simple pero flexible para permitir tanto:

El TreeBuilder vinculado anteriormente es parte de un paquete de demostración de CAS que realiza una derivación simbólica. También hay un ejemplo de intérprete BÁSICO y comencé a construir un intérprete TypeScript usándolo.


Para mi proyecto universitario, estaba buscando un analizador / evaluador que admitiera tanto fórmulas básicas como ecuaciones más complicadas (especialmente operadores iterados). Encontré una biblioteca de código abierto muy agradable para JAVA y .NET llamada mXparser. Daré algunos ejemplos para hacer una idea de la sintaxis. Para más instrucciones, visite el sitio web del proyecto (especialmente la sección de tutoriales).

http://mathparser.org/

http://mathparser.org/mxparser-tutorial/

http://mathparser.org/api/

Y algunos ejemplos

1 - furmula simple

Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2"); double v = e.calculate()

2 - Argumentos y constantes definidos por el usuario

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 - Funciones definidas por el usuario

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 - Iteración

Expression e = new Expression("sum( i, 1, 100, sin(i) )"); double v = e.calculate()

Atentamente


Parece que JEP debería hacer el trabajo


Podrías echar un vistazo al framework Symja :

ExprEvaluator util = new ExprEvaluator(); IExpr result = util.evaluate("10-40"); System.out.println(result.toString()); // -> "-30"

Tenga en cuenta que definitivamente se pueden evaluar expresiones más complejas:

// 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


Pruebe el siguiente código de muestra utilizando el motor de Javascript de JDK1.6 con manejo de inyección de código.

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(); }


Puede evaluar expresiones fácilmente si su aplicación Java ya accede a una base de datos, sin usar ningún otro JAR.

Algunas bases de datos requieren que use una tabla ficticia (por ejemplo, la tabla "dual" de Oracle) y otras le permitirán evaluar expresiones sin "seleccionar" de ninguna tabla.

Por ejemplo, en Sql Server o Sqlite

select (((12.10 +12.0))/ 233.0) amount

y en oráculo

select (((12.10 +12.0))/ 233.0) amount from dual;

La ventaja de usar un DB es que puede evaluar muchas expresiones al mismo tiempo. Además, la mayoría de los DB le permitirán usar expresiones altamente complejas y también tendrá una serie de funciones adicionales que se pueden llamar según sea necesario.

Sin embargo, el rendimiento puede verse afectado si es necesario evaluar muchas expresiones individuales de manera individual, especialmente cuando la base de datos se encuentra en un servidor de red.

Lo siguiente aborda el problema de rendimiento en cierta medida, mediante el uso de una base de datos en memoria Sqlite.

Aquí hay un ejemplo completo de trabajo en 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();

Por supuesto, podría extender el código anterior para manejar múltiples cálculos al mismo tiempo.

ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");


Qué tal algo como esto:

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); } }

y hacer lo mismo para todos los demás operadores matemáticos en consecuencia ..


Se pueden usar bibliotecas externas como RHINO o NASHORN para ejecutar javascript. Y javascript puede evaluar una fórmula simple sin forzar la cadena. Sin impacto en el rendimiento también si el código está bien escrito. A continuación se muestra un ejemplo con 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); }


Si vamos a implementarlo, entonces podemos usar el algoritmo siguiente:

  1. Mientras que todavía hay tokens para leer,

    1.1 Consigue el siguiente token. 1.2 Si el token es:

    1.2.1 Un número: empujarlo en la pila de valores.

    1.2.2 Una variable: obtenga su valor, y presione la pila de valores.

    1.2.3 Un paréntesis izquierdo: empújelo sobre la pila del operador.

    1.2.4 Un paréntesis derecho:

    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 Un operador (llámelo 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.

  2. Mientras la pila del operador no está vacía, 1 Extraiga el operador de la pila del operador. 2 Pop la pila de valores dos veces, obteniendo dos operandos. 3 Aplicar el operador a los operandos, en el orden correcto. 4 Empuje el resultado en la pila de valores.

  3. En este punto, la pila del operador debe estar vacía, y la pila de valores debe tener solo un valor, que es el resultado final.


También puedes probar el intérprete BeanShell :

Interpreter interpreter = new Interpreter(); interpreter.eval("result = (7+21*6)/(32-27)"); System.out.println(interpreter.get("result"));


Una clase de Java que puede evaluar expresiones matemáticas:

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)))")); } }


Este artículo apunta a 3 enfoques diferentes, uno que es JEXL de Apache y permite secuencias de comandos que incluyen referencias a objetos Java.


HERE hay otra biblioteca de código abierto en GitHub llamada EvalEx.

A diferencia del motor de JavaScript, esta biblioteca está enfocada en evaluar expresiones matemáticas solamente. Además, la biblioteca es extensible y admite el uso de operadores booleanos y paréntesis.


import java.util.*; StringTokenizer st; int ans; public class check { 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; } //......... }


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)); }

}