Saltar al contenido

Cómo evaluar una expresión matemática dada en string ¿forma?

Posterior a consultar con especialistas en esta materia, programadores de varias ramas y profesores dimos con la respuesta a la interrogande y la compartimos en esta publicación.

Solución:

Con JDK1.6, puede usar 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));
     

he escrito esto eval método de expresiones aritméticas para responder a esta pregunta. Hace sumas, restas, multiplicaciones, divisiones, exponenciaciones (usando el ^ símbolo), y algunas funciones básicas como sqrt. Es compatible con la agrupación mediante ()y obtiene las reglas de asociatividad y precedencia de operadores correctas.

public static double eval(final String str) 
    return new Object()  expression `+` term .parse();

Ejemplo:

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

Salida: 7.5 (que es correcto)


El analizador es un analizador descendente recursivo, por lo que internamente utiliza métodos de análisis independientes para cada nivel de precedencia de operadores en su gramática. lo guardé corto por lo que es fácil de modificar, pero aquí hay algunas ideas con las que podría querer 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 pasada al eval método, como un Map variables.

  • Compilación y evaluación separadas:

    ¿Qué pasaría si, habiendo agregado soporte para variables, quisiera evaluar la misma expresión millones de veces con variables modificadas, 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 doubles, por lo que en su lugar 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 Expression objetos que representan la expresión compilada (un árbol de sintaxis abstracta). Luego puede compilarlo una vez y evaluarlo repetidamente con diferentes valores:

    public static void main(String[] args) 
        Map 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 vez de doublepodría cambiar el evaluador para usar algo más poderoso como BigDecimal, o una clase que implementa números complejos, o números racionales (fracciones). Incluso podrías usar Object, lo que permite cierta combinación de tipos de datos en expresiones, como un lenguaje de programación real. 🙂


Todo el código en esta respuesta liberado al dominio público. ¡Diviértete!

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

Crear un analizador de descenso recursivo es un muy buen ejercicio de aprendizaje.

Recuerda algo, que tienes la capacidad de valorar este post si diste con el hallazgo.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *