2016-12-05 89 views
2

我想要解答這個問題的更多幫助,Evaluating a math expression given in string form。用戶@Boann用一個非常有趣的算法回答了這個問題,他也指出可以改變以接受變量。我已經設法改變它,並讓它起作用,但不知道他是如何分離編譯和評估。這裏是我的代碼:用變量評估數學表達式。 (java 8)

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

如果你看看他的回答,他的主要

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

他在這一行Expression exp = parse("x^2 - x + 2", variables);調用函數parse編譯一次表達,並使用了它多次評估具有獨特的x值。 parse函數指的是什麼。

ps:我評論過用戶問題沒有回覆。

+0

「是什麼解析函數參考」 '表達式解析(){ nextChar(); 表達式x = parseExpression(); if(pos Thibstars

+0

我不認爲這是正確的,因爲該功能對主要不可見,而他呼叫的'parse'功能需要兩個參數@Tststars –

+0

你是對的。它不應該是'eval()'? – Thibstars

回答

1

對不起,混亂。我提到的「parse」函數只是現有的eval函數,但因爲它返回Expression對象而被重命名。

所以你必須:

並通過調用它:

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:在類級別

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

然後某處,初始化functions地圖:

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

(這也將是可以在分析器中將functions映射定義爲局部變量,但是在每個解析過程中會增加一點點開銷。)

+0

非常感謝。這個選項比使用javscript快得多。 –

2

我認爲Expression持有地圖參考的實際代碼沒有公佈在答案上。

爲了使該示例代碼正常工作,表達式必須具有某種內存。實際上,代碼操縱地圖,並且由於表達式保存對它的引用,隨後每次調用表達式的eval - 方法都將採用調整後的值。

因此,爲了使代碼正常工作,您首先需要一個表達式實現,它包含一個映射引用。但要小心這樣一種表達可能產生的任何副作用。

因此,對於這個特定的例子,代碼必須是類似於以下內容的東西(沒有時間來完全查看它是否可以工作,但您會明白:重要的是表達式持有一個引用這是從外面改變):

static Expression parse(String expression, Map<String, String> variables) { 
    return new PseudoCompiledExpression(expression, variables); 
} 
static class PseudoCompiledExpression implements Expression { 
    Map<String, String> variables; 
    Expression wrappedExpression; 
    PseudoCompiledExpression(String expression, Map<String, String> variables) { 
     this.variables = variables; 
     wrappedExpression = eval(expression, variables); 
    } 
    public double eval() { 
     // the state of the used map is altered from outside... 
     return wrappedException.eval(); 
    } 
+0

感謝您的幫助,但Boann澄清了他的答案。 –

0

我發現上述語法不適用於求冪。這其中的工作原理:

public class BcInterpreter { 

static final String BC_SPLITTER = "[\\^\\(\\/\\*\\-\\+\\)]"; 
static Map<String,Double> variables = new HashMap<>(); 
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))); 
    functions.put("round", x -> Math.round(x)); 
    functions.put("abs", x -> Math.abs(x)); 
    functions.put("ceil", x -> Math.ceil(x)); 
    functions.put("floor", x -> Math.floor(x)); 
    functions.put("log", x -> Math.log(x)); 
    functions.put("exp", x -> Math.exp(x)); 
    // TODO: add more unary functions here. 
} 

/** 
* Parse the expression into a lambda, and evaluate with the variables set from fields 
* in the current row. The expression only needs to be evaluated one time. 
* @param recordMap 
* @param fd 
* @param script 
* @return 
*/ 
static String materialize(Map<String, String> recordMap, FieldDesc fd, String script){ 
    // parse the expression one time and save the lambda in the field's metadata 
    if (fd.get("exp") == null) { 
     fd.put("exp", parse(script, variables)); 
    } 
    // set the variables to be used with the expression, once per row 
    String[] tokens = script.split(BC_SPLITTER); 
    for(String key : tokens) { 
     if (key != null) { 
      String val = recordMap.get(key.trim()); 
      if (val != null) 
       variables.put(key.trim(), Double.parseDouble(val)); 
     } 
    } 
    // evaluate the expression with current row's variables 
    return String.valueOf(((Expression)(fd.get("exp"))).eval()); 
} 

@FunctionalInterface 
interface Expression { 
    double eval(); 
} 

static Map<String,Double> getVariables(){ 
    return variables; 
} 

public static Expression parse(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 = base | base '^' base 
     // base = '-' base | '+' base | number | identifier | function factor | '(' expression ')' 

     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(){ 
      Expression x = parseBase(); 
      for(;;){ 
       if (eat('^')){ 
        Expression a = x, b = parseBase(); 
        x = (()->Math.pow(a.eval(),b.eval())); 
       }else{ 
        return x; 
       } 
      } 
     } 
     Expression parseBase(){ 
      int startPos = this.pos; 
      Expression x; 
      if (eat('-')){ 
       Expression b = parseBase(); 
       x = (()-> (-1)*b.eval()); 
       return x; 
      }else if (eat('+')){ 
       x = parseBase(); 
       return x; 
      } 
      if (eat('(')) { // parentheses 
       x = parseExpression(); 
       eat(')'); 
       return x; 
      } 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; 
       return x; 
      } else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') { // functions and variables 
       while (ch >= 'a' && ch <= 'z' || 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); 
       } 
       return x; 
      }else { 
       throw new RuntimeException("Unexpected: " + (char)ch); 
      } 
     } 
    }.parse(); 
} 
} 
+0

你好,我只是測試了負指數,他們似乎沒有正常工作,但積極的做。雖然我不需要它,但很高興知道。謝謝 –