用变量评估数学表达式。 (java 8) [英] Evaluating a math expression with variables. (java 8)

查看:97
本文介绍了用变量评估数学表达式。 (java 8)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于此问题的解答,我需要其他帮助,评估以字符串形式给出的数学表达式。 @Boann用户使用非常有趣的算法回答了这个问题,他还指出可以更改为接受变量。我设法对其进行了修改,并使它能够正常工作,但不知道他是如何将编译和评估分开的。这是我的代码:

I would like additional help on a answer to this question, Evaluating a math expression given in string form. The user @Boann answered the question with a very interesting algorithm that he also points out can be altered to accept variables. I've managed to alter it and get it to work, but dont know how he separates the compilation and evaluation. Here's my code:

import java.util.HashMap;
import java.util.Map;

public class EvaluateExpressionWithVariabels {

@FunctionalInterface
interface Expression {
    double eval();
}

public static void main(String[] args){
    Map<String,Double> variables = new HashMap<>();     
    for (double x = 100; x <= +120; x++) {
        variables.put("x", x);
        System.out.println(x + " => " + eval("x+(sqrt(x))",variables).eval());
    }
}

public static Expression eval(final String str,Map<String,Double> variables) {
    return new Object() {
        int pos = -1, ch;

        //if check pos+1 is smaller than string length ch is char at new pos
        void nextChar() {
            ch = (++pos < str.length()) ? str.charAt(pos) : -1;
        }

        //skips 'spaces' and if current char is what was searched, if true move to next char return true
        //else return false
        boolean eat(int charToEat) {
            while (ch == ' ') nextChar();
            if (ch == charToEat) {
                nextChar();
                return true;
            }
            return false;
        }


        Expression parse() {
            nextChar();
            Expression 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

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

        Expression parseTerm() {
            Expression x = parseFactor();
            for (;;) {
                if (eat('*')){
                     Expression a = x, b = parseFactor(); // multiplication
                     x = (() -> a.eval() * b.eval());
                }
                else if(eat('/')){
                     Expression a = x, b = parseFactor(); // division
                     x = (() -> a.eval() / b.eval());
                }
                else return x;
            }
        }

        Expression parseFactor() {
            if (eat('+')) return parseFactor(); // unary plus
            if (eat('-')){
                Expression b = parseFactor(); // unary minus
                return (() -> -1 * b.eval());
            }

            Expression 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();
                }
                double xx = Double.parseDouble(str.substring(startPos, this.pos));
                x = () -> xx;
            } else if (ch >= 'a' && ch <= 'z') { // functions
                while (ch >= 'a' && ch <= 'z') nextChar();
                String func = str.substring(startPos, this.pos);

                if ( variables.containsKey(func)){
                    x = () -> variables.get(func);
                }else{
                    double xx = parseFactor().eval();
                    if (func.equals("sqrt")) x = () -> Math.sqrt(xx);
                    else if (func.equals("sin")) x = () -> Math.sin(Math.toRadians(xx));
                    else if (func.equals("cos")) x = () -> Math.cos(Math.toRadians(xx));
                    else if (func.equals("tan")) x = () -> Math.tan(Math.toRadians(xx));                    
                    else throw new RuntimeException("Unknown function: " + func);
                }
            } else {
                throw new RuntimeException("Unexpected: " + (char)ch);
            }

            if (eat('^')){ 
                x = () -> {
                    double d =  parseFactor().eval();
                    return Math.pow(d,d); // exponentiation
                };
            }

            return  x;
        }
    }.parse();
}
}

如果看看他的回答,他的主要意思

If you take a look at his answer his main

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

他调用函数 parse 在此行上 Expression exp = parse( x ^ 2--x + 2,变量); 一次编译表达式并使用for使用唯一的x值对其进行多次评估。 parse 函数指的是什么。

He calls function parse on this line Expression exp = parse("x^2 - x + 2", variables); to compile the expression once and uses the for to evaluate it multiple times with a unique x values. What does the parse function refer to.

ps:我对用户问题发表了评论,没有任何答复。 / p>

ps: I have commented on the users question with no reply.

推荐答案

抱歉,造成混乱。我提到的 parse 函数只是现有的 eval 函数,但是由于它返回了表达式对象。

Sorry for the confusion. The "parse" function I referred to is simply the existing eval function, but renamed since it returns an Expression object.

因此,您将拥有:

public static Expression parse(String str, Map<String,Double> variables) { ... }

并通过以下方式调用它:

And invoke it by:

Map<String,Double> variables = new HashMap<>();
Expression exp = parse("x+(sqrt(x))", variables);
for (double x = 100; x <= +120; x++) {
    variables.put("x", x);
    System.out.println(x + " => " + exp.eval());
}

另一件事:有必要在解析时知道名称是否指代变量或函数,以便知道它是否接受参数,但是在解析期间您不能在变量映射上调用 containsKey 在调用 exp.eval()之前不会出现在地图中!一种解决方案是改为在地图中放置函数,因此您可以在其上调用 containsKey

One other thing: It's necessary to know at parse time whether a name refers to a variable or a function, in order to know whether or not it takes an argument, but you can't call containsKey on the variables map during the parse, since the variables might not be present in the map until exp.eval() is called! One solution is to put functions in a map instead, so you can call containsKey on that:

    } else if (ch >= 'a' && ch <= 'z') { // functions and variables
        while (ch >= 'a' && ch <= 'z') nextChar();
        String name = str.substring(startPos, this.pos);
        if (functions.containsKey(name)) {
            DoubleUnaryOperator func = functions.get(name);
            Expression arg = parseFactor();
            x = () -> func.applyAsDouble(arg.eval());
        } else {
            x = () -> variables.get(name);
        }
    } else {

然后在类级别的某个地方初始化功能映射:

And then somewhere at class level, initialize the functions map:

private static final Map<String,DoubleUnaryOperator> functions = new HashMap<>();
static {
    functions.put("sqrt", x -> Math.sqrt(x));
    functions.put("sin", x -> Math.sin(Math.toRadians(x)));
    functions.put("cos", x -> Math.cos(Math.toRadians(x)));
    functions.put("tan", x -> Math.tan(Math.toRadians(x)));
}

(也可以定义函数映射为解析器内部的局部变量,但这会在每次解析过程中增加一些开销。)

(It would also be okay to define the functions map as a local variable inside the parser, but that adds a little bit more overhead during each parse.)

这篇关于用变量评估数学表达式。 (java 8)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆