Reputation: 105
I have this as a server response:
"[(sin(1+3*4)+2)/7]+10"
This response means that 1, 3, 4, 2, 7 and 10 are some sort of index items on a DB(mysql) and I need to evaluate it a lot of times. I was looking for a good lib in java and I found exp4j, expr, jep and jeval like a Mathematical expressions parser but I don’t see where’s the option to get the those "index", because the server just give me that string but don’t give me the "index items" that I need to consult in my DB. Please, help :(
Extra: 1, 3, 4, 2, 7 and 10 are variables, I need to get it (not always numbers) because these variable names are my indexes in a DB. When I get the names I create a sql query to get the real values of that variables.
It's like this...
ParseMath function = new ParseMath("[(sin(1+3*4)+2)/7]+10");
String[] variables = function.getVariables();
System.out.println(values) = 1, 3, 4, 2, 7, 10
And later...
String[] realValues = SqlQuery(variables);
for(int i=0; i<variables.lenght(); i++){
function.setValue(variable[i],realValue[i]);
}
double result = function.exec();
PS: The functions and methods that I wrote don't exist, I just put that like a context of my problem...
Upvotes: 0
Views: 922
Reputation: 426
I'm a little rusty on my regex, but I think it is the most effective way to do this. Regex will get you the name of the variables and numbers in your expression, and then you can use whatever facilities the expression lib gives you to substitute values back in. Note that you need to know every function that the server can return so that you can exclude them from your results (they look like variables).
public static void main( String[] args ) {
String expression = "[(sin(1+3*4)+2)/7]+10";
LinkedList<String> vars = new LinkedList<>();
Pattern p = Pattern.compile("(?:[^0-9a-zA-Z]|\\G|^)([0-9a-zA-Z]+)(?:[^0-9a-zA-Z]|$)");
Matcher m = p.matcher( expression );
while(m.find()) {
vars.add( m.group( 1 ) );
}
for(String s : vars ) {
// Here's where you'd filter out the functions like "sin", "cos", etc.
System.out.println( s );
}
}
I'd recommend testing this with a number of examples you get to make sure there aren't any holes in my regular expression.
Upvotes: 0
Reputation: 18793
Most expression parsers will treat the numbers as numbers. If you want them to be treated as variables, replace or prefix them with letters, i.e. do something like this:
ParseMath function = new ParseMath(replaceVariables("[(sin(1+3*4)+2)/7]+10"));
where replaceVariables would be something like this:
String replaceVariables(String expr) {
StringBuilder sb = new StringBuilder();
boolean wasDigit = false;
for (int i = 0; i < expr.length; i++) {
char c = sb.charAt(i);
if (c >= '0' && c <= '9') {
if (!wasDigit) {
sb.append('x');
}
wasDigit = true;
} else if (c == '[') {
c = '(';
} else if (c == ']') {
c = ')';
}
sb.append(c);
}
return sb.toString();
}
This should turn the example expression into ((sin(x1+x3*x4)+x2)/x7)+x10
which has a better chance of being recognized by expression parsers.
Note that you need to do the same transformation when you set the variables, i.e. if your server response is in a String array realValues
, you'll need to do something similar to this to set them:
for (int i = 0; i < realValues.length; i++) {
function.setValue("x" + i, Double.parseDouble(realValues[i]));
}
double result = function.exec();
Upvotes: 2