J'essaie d'écrire une routine Java pour évaluer des expressions mathématiques simples à partir de String
valeurs comme:
"5+3"
"10-40"
"10*3"
Je veux éviter beaucoup de déclarations if-then-else. Comment puis-je faire ceci?
J'essaie d'écrire une routine Java pour évaluer des expressions mathématiques simples à partir de String
valeurs comme:
"5+3"
"10-40"
"10*3"
Je veux éviter beaucoup de déclarations if-then-else. Comment puis-je faire ceci?
Réponses:
Avec JDK1.6, vous pouvez utiliser le moteur Javascript intégré.
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();");
- va écrire un fichier via JavaScript dans (par défaut) le répertoire courant du programme
J'ai écrit cette eval
méthode pour les expressions arithmétiques pour répondre à cette question. Il fait l'addition, la soustraction, la multiplication, la division, l'exponentiation (en utilisant le ^
symbole), et quelques fonctions de base comme sqrt
. Il prend en charge le regroupement à l'aide de (
... )
et obtient la priorité de l'opérateur et les règles d' associativité correctes.
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();
}
Exemple:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Sortie: 7,5 (ce qui est correct)
L'analyseur est un analyseur de descente récursif , donc utilise en interne des méthodes d'analyse distinctes pour chaque niveau de priorité de l'opérateur dans sa grammaire. Je l'ai gardé court donc c'est facile à modifier, mais voici quelques idées que vous voudrez peut-être développer avec:
Variables:
Le bit de l'analyseur qui lit les noms des fonctions peut facilement être modifié pour gérer également les variables personnalisées, en recherchant les noms dans une table de variables passée à la eval
méthode, comme a Map<String,Double> variables
.
Compilation et évaluation séparées:
Et si, après avoir ajouté la prise en charge des variables, vous vouliez évaluer la même expression des millions de fois avec des variables modifiées, sans l'analyser à chaque fois? C'est possible. Définissez d'abord une interface à utiliser pour évaluer l'expression précompilée:
@FunctionalInterface
interface Expression {
double eval();
}
Maintenant, changez toutes les méthodes qui retournent double
s, donc à la place, elles retournent une instance de cette interface. La syntaxe lambda de Java 8 fonctionne très bien pour cela. Exemple d'une des méthodes modifiées:
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;
}
}
}
Cela construit un arbre récursif d' Expression
objets représentant l'expression compilée (un arbre de syntaxe abstraite ). Ensuite, vous pouvez le compiler une fois et l'évaluer à plusieurs reprises avec différentes valeurs:
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());
}
}
Différents types de données:
Au lieu de double
, vous pouvez changer l'évaluateur pour utiliser quelque chose de plus puissant BigDecimal
, ou une classe qui implémente des nombres complexes, ou des nombres rationnels (fractions). Vous pouvez même utiliser Object
, permettant un mélange de types de données dans les expressions, tout comme un véritable langage de programmation. :)
Tout le code de cette réponse est tombé dans le domaine public . S'amuser!
double x = parseTerm();
évalue l'opérateur de gauche, puis for (;;) {...}
évalue les opérations successives du niveau d'ordre réel (addition, soustraction). La même logique est et dans la méthode parseTerm. Le parseFactor n'a pas de niveau suivant, il n'y a donc que des évaluations de méthodes / variables ou en cas de paranthèse - évaluer la sous-expression. La boolean eat(int charToEat)
méthode vérifie l'égalité du caractère du curseur actuel avec le caractère charToEat, si égal retourne vrai et déplace le curseur au caractère suivant, j'utilise le nom 'accept' pour cela.
La bonne façon de résoudre ce problème est d' utiliser un lexer et un analyseur . Vous pouvez en écrire vous-même des versions simples, ou ces pages contiennent également des liens vers des lexers et des analyseurs Java.
La création d'un analyseur de descente récursive est un très bon exercice d'apprentissage.
Pour mon projet universitaire, je cherchais un analyseur / évaluateur supportant à la fois les formules de base et les équations plus compliquées (notamment les opérateurs itérés). J'ai trouvé une très belle bibliothèque open source pour JAVA et .NET appelée mXparser. Je vais donner quelques exemples pour vous faire une idée de la syntaxe, pour plus d'instructions, veuillez visiter le site Web du projet (en particulier la section tutoriel).
https://mathparser.org/mxparser-tutorial/
Et quelques exemples
1 - Furmula simple
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - Arguments et constantes définis par l'utilisateur
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 - Fonctions définies par l'utilisateur
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 - Itération
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Trouvé récemment - au cas où vous voudriez essayer la syntaxe (et voir le cas d'utilisation avancée), vous pouvez télécharger l' application Scalar Calculator qui est propulsée par mXparser.
Meilleures salutations
ICI est une autre bibliothèque open source sur GitHub nommée EvalEx.
Contrairement au moteur JavaScript, cette bibliothèque se concentre uniquement sur l'évaluation des expressions mathématiques. De plus, la bibliothèque est extensible et prend en charge l'utilisation d'opérateurs booléens ainsi que les parenthèses.
Vous pouvez également essayer l' interpréteur BeanShell :
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
Vous pouvez évaluer facilement des expressions si votre application Java accède déjà à une base de données, sans utiliser d'autres JAR.
Certaines bases de données nécessitent que vous utilisiez une table fictive (par exemple, la table "double" d'Oracle) et d'autres vous permettront d'évaluer des expressions sans "sélectionner" dans une table.
Par exemple, dans Sql Server ou Sqlite
select (((12.10 +12.0))/ 233.0) amount
et dans Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
L'avantage d'utiliser une base de données est que vous pouvez évaluer plusieurs expressions en même temps. De plus, la plupart des bases de données vous permettront d'utiliser des expressions très complexes et auront également un certain nombre de fonctions supplémentaires qui peuvent être appelées si nécessaire.
Cependant, les performances peuvent souffrir si de nombreuses expressions uniques doivent être évaluées individuellement, en particulier lorsque la base de données se trouve sur un serveur réseau.
Ce qui suit résout le problème de performances dans une certaine mesure, en utilisant une base de données en mémoire Sqlite.
Voici un exemple de travail complet 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();
Bien sûr, vous pouvez étendre le code ci-dessus pour gérer plusieurs calculs en même temps.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
Cet article présente différentes approches. Voici les 2 approches clés mentionnées dans l'article:
Permet les scripts qui incluent des références aux objets java.
// 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();
}
}
Une autre façon consiste à utiliser Spring Expression Language ou SpEL qui fait beaucoup plus avec l'évaluation des expressions mathématiques, donc peut-être un peu exagéré. Il n'est pas nécessaire d'utiliser Spring Framework pour utiliser cette bibliothèque d'expressions car elle est autonome. Copie d'exemples de la documentation 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
Lisez des exemples SpEL plus concis ici et la documentation complète ici
si nous allons l'implémenter, nous pouvons utiliser l'algorithme ci-dessous: -
Bien qu'il reste des jetons à lire,
1.1 Obtenez le prochain jeton. 1.2 Si le jeton est:
1.2.1 Un nombre: poussez-le sur la pile de valeurs.
1.2.2 Une variable: obtenez sa valeur et poussez sur la pile de valeurs.
1.2.3 Une parenthèse gauche: poussez-la sur la pile d'opérateurs.
1.2.4 Une parenthèse droite:
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 opérateur (appelez-le 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.
Tant que la pile d'opérateurs n'est pas vide, 1 Pop l'opérateur de la pile d'opérateurs. 2 Pop la pile de valeurs deux fois, obtenant deux opérandes. 3 Appliquez l'opérateur aux opérandes, dans le bon ordre. 4 Poussez le résultat sur la pile de valeurs.
À ce stade, la pile d'opérateurs doit être vide et la pile de valeurs ne doit contenir qu'une seule valeur, ce qui est le résultat final.
Ceci est une autre alternative intéressante https://github.com/Shy-Ta/expression-evaluator-demo
L'utilisation est très simple et fait le travail, par exemple:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Je pense que quoi que vous fassiez, cela impliquera beaucoup de déclarations conditionnelles. Mais pour des opérations uniques comme dans vos exemples, vous pouvez le limiter à 4 si des instructions avec quelque chose comme
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
Cela devient beaucoup plus compliqué lorsque vous souhaitez gérer plusieurs opérations comme "4 + 5 * 6".
Si vous essayez de construire une calculatrice, je ferais mieux de passer chaque section du calcul séparément (chaque nombre ou opérateur) plutôt que comme une seule chaîne.
Il est trop tard pour répondre mais je suis tombé sur la même situation pour évaluer l'expression en java, cela pourrait aider quelqu'un
MVEL
fait l'évaluation des expressions au moment de l'exécution, nous pouvons écrire un code java String
pour le faire évaluer.
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);
Vous pourriez jeter un œil au framework Symja :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Notez que des expressions définitivement plus complexes peuvent être évaluées:
// 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
Essayez l'exemple de code suivant en utilisant le moteur Javascript de JDK1.6 avec la gestion de l'injection de code.
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;
}
}
}
Cela complète en fait la réponse donnée par @Boann. Il a un léger bug qui fait que "-2 ^ 2" donne un résultat erroné de -4.0. Le problème pour cela est le point auquel l'exponentiation est évaluée dans le sien. Déplacez simplement l'exponentiation vers le bloc de parseTerm (), et tout ira bien. Jetez un œil à ce qui suit, qui est la réponse de @ Boann légèrement modifiée. La modification est dans les commentaires.
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
est en fait normal, et pas un bug. Il est groupé comme -(2^2)
. Essayez-le sur Desmos, par exemple. Votre code introduit en fait plusieurs bogues. La première est que ^
ne regroupe plus de droite à gauche. En d'autres termes, 2^3^2
est supposé grouper comme 2^(3^2)
parce qu'il ^
est associatif à droite, mais vos modifications le rendent groupé (2^3)^2
. La seconde est celle qui ^
est censée avoir une priorité plus élevée que *
et /
, mais vos modifications la traitent de la même manière. Voir 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));
}
}
Que diriez-vous quelque chose comme ça:
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);
}
}
et faire la même chose pour tous les autres opérateurs mathématiques en conséquence ..
Il est possible de convertir n'importe quelle chaîne d'expression en notation infixe en une notation postfixe en utilisant l' algorithme de shunting-yard de Djikstra . Le résultat de l'algorithme peut alors servir d'entrée à l' algorithme de postfixe avec retourne le résultat de l'expression.
J'ai écrit un article à ce sujet ici, avec une implémentation en java
Encore une autre option: https://github.com/stefanhaustein/expressionparser
J'ai implémenté cela pour avoir une option simple mais flexible pour permettre à la fois:
Le TreeBuilder lié ci-dessus fait partie d'un package de démonstration CAS qui fait une dérivation symbolique. Il existe également un exemple d' interpréteur BASIC et j'ai commencé à construire un interpréteur TypeScript en l' utilisant.
Une classe Java qui peut évaluer des expressions mathématiques:
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)))"));
}
}
Une bibliothèque externe comme RHINO ou NASHORN peut être utilisée pour exécuter javascript. Et javascript peut évaluer une formule simple sans parceller la chaîne. Aucun impact sur les performances également si le code est bien écrit. Voici un exemple avec 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; }
//.........
}