完整的Java表达式算法

---扩充容易

本文的表达式求值采用标准的算法。首先从最简单的表达式求值开始,到后面的高级表达式求值。大郅算法如下,首先将表达式转换为后序表达式,然后对后序表达式求值。表示式求值的关键步骤式表达式语义的解析和分割,而对于表达式的求值反而简单。

在实际应用中,经常会有如下的场景:

1、对一行数据进行运算,例如:总价=单价*数量。

2、对集合数据进行运算,例如:平均销售价格=sum(单价*数量)/sum(数量)。

3、对集合数据进行运算,例如:全校平均成绩=(sum(数学平均分)*sum(数学考试人数)+sum(语文平均分)*sum(语文考试人数))/(sum(数学考试人数+语文考试人数)。

本文中的算法完全可以解决此类问题。

一、将公式变换为后序表达式

1)检查输入的下一元素。

2)假如是个操作数,输出。

3)假如是个开括号,将其压栈。

4)假如是个运算符,则

i) 假如栈为空,将此运算符压栈。

ii) 假如栈顶是开括号,将此运算符压栈。

iii) 假如此运算符比栈顶运算符优先级高,将此运算符压入栈中。

iv) 否则栈顶运算符出栈并输出,重复步骤4。

5)假如是个闭括号,栈中运算符逐个出栈并输出,直到遇到开括号。开括号出栈并丢弃。

6)假如输入还未完毕,跳转到步骤1。

7)假如输入完毕,栈中剩余的所有操作符出栈并输出它们。

算法如下:

public static Stack<String> convertToPostfix (StringSplit x) {
    String s = x.getNext () ;
    Stack<String> st = new Stack<String> () ;
    Stack<String> rSt = new Stack<String> () ;
    while (s != null) {
      if (isBlank (s)) {
        //break;
      } else if (isOpenParenthesis (s)) {
        st.push (s) ;
      } else if (isCloseParenthesis (s)) {
        String as = null ;
        if (st.isEmpty ()) {
          System.out.println ("错误:缺少(") ;
        } else {
          as = st.pop () ;
          while (!isOpenParenthesis (as)) {
            rSt.push (as) ;
            if (st.isEmpty ()) {
              System.out.println ("错误:缺少(") ;
              break ;
            } else {
              as = st.pop () ;
            }
          }
        }
      } else if (isOperator (s)) {
        if (!st.isEmpty ()) {
          String as = st.pop () ;
          while (priority (as) >= priority (s)) {
            rSt.push (as) ;
            if (st.isEmpty ()) {
              as = null ;
            } else {
              as = st.pop () ;
            }
          }
          if (as != null) {
            st.push (as) ;
          }
          st.push (s) ;
        } else {
          st.push (s) ;
        }
      } else {
        rSt.push (s) ;
      }
      s = x.getNext () ;
    }
 
    while (!st.isEmpty ()) {
      String as = st.pop () ;
      if (isOpenParenthesis (as)) {
        System.out.println ("错误:缺少)") ;
      } else if (isCloseParenthesis (as)) {
        System.out.println ("错误:缺少(") ;
      } else {
        rSt.push (as) ;
      }
    }
    Stack<String> nSt = new Stack<String> () ;
    while (!rSt.empty ()) {
      nSt.push (rSt.pop ()) ;
    }
    return nSt ;
  }

 

类StringSplit是接口,目的是将表示式进行拆分,拆分为操作数、括号、运算符。本文中实现了单一字符解析、多字符解析以及函数解析等功能。以满足不同的应用场景。

package net.csdn.blog.z3h.util ;
 
public interface StringSplit {
  String getNext () ;
}

 

二、对后序表达式进行求值计算

1)初始化一个空堆栈

2)从左到右读入后缀表达式

i)如果字符是一个操作数,把它压入堆栈。

ii)如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。

3)到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。

public static double numberCalculate (Stack<String> st) {
    Stack<String> tSt = new Stack<String> () ;
    while (!st.empty ()) {
      String a = st.pop () ;
      //System.out.println (a) ;
      if (isOperator (a)) {
        double d2 = Double.parseDouble (tSt.pop ()) ;
        double d1 = Double.parseDouble (tSt.pop ()) ;
        double d3 = 0 ;
        if ("+".equals (a)) {
          d3 = d1 + d2 ;
        } else if ("-".equals (a)) {
          d3 = d1 - d2 ;
        } else if ("*".equals (a)) {
          d3 = d1 * d2 ;
        } else if ("/".equals (a)) {
          d3 = d1 / d2 ;
        }
        tSt.push (String.valueOf (d3)) ;
      } else {
        tSt.push (a) ;
      }
    }
    return Double.parseDouble (tSt.pop ()) ;
  }

 

 

三、完整的求值

 

单字符表达式分割对象。

package net.csdn.blog.z3h.util ;
 
public class CharSplit
    implements StringSplit {
  private String s ;
 
  private int pos = 0 ;
 
  public CharSplit (String s) {
    this.s = s ;
  }
 
  public String getNext () {
    if (pos < s.length ()) {
      return s.charAt (pos++) + "" ;
    } else {
      return null ;
    }
  }
}

 

表达式求值示例:

//单字符求值。
            //表达式分割对象
           StringSplit split = new CharSplit("2*3/(2-1)+5*(4-1)");
           //将表达式转化位后序表达式
           Stack<String> postFixStack = Expression.convertToPostfix(split);
           //对后序表达式进行运算
           double result = Expression.numberCalculate(postFixStack);
           System.out.println (result) ;

 

四、高级表达式应用

 

在实际应用中,经常会有如下的场景:

1、 对一行数据进行运算,例如总价=单价*数量。

2、 对集合数据进行运算,例如:平均销售价格=sum(单价*数量)/sum(数量)。

StringSplit split = new FunctionSplit (
               "(a12+b23+c34)/($9+c34-12-b23)+9") ; //$9 表示"9",9表示9
           Stack<String> postFixStack = Expression.convertToPostfix (split);
           double result =Expression.rowCalculate (rowData ,postFixStack);
           System.out.println (result) ;

对集合数据进行运算示例:

List<Map<String,Double>> dataTable = new ArrayList<Map<String,Double>> () ;
           ......
 
           StringSplit split = new FunctionSplit (
               "(sum('a'+'b'+'c'))/rowcount()-sum('a')") ;
           Stack<String> postFixStack = Expression.convertToPostfix (split);
           double result = Expression.tableCalculate (dataTable,postFixStack);
           System.out.println(result);

 

对一行数据求值的算法

/**
   * 对Map<String,Double>求值.
   * 例如:相应的表达式为
   * “(a+b)/2”表示列a和列b的和的平均
   * “(a+b)/$4”表示列a与列b求和,然后除 列4
   * @param map DataObject
   * @param st Stack
   * @return double
   */
  public static double rowCalculate (Map<String,Double> map , Stack<String> sourceStack) {
      Stack<String> calStack = new Stack<String> () ;
      @SuppressWarnings("unchecked")
      Stack<String> cloneStack = (Stack<String>)sourceStack.clone();; //保证之前的栈不变.
      while (!cloneStack.empty ()) {
        String a = cloneStack.pop () ;
        //System.out.println (a) ;
        if (isOperator (a)) {
          double d2 = doValues (map , calStack.pop ()) ;
          double d1 = doValues (map , calStack.pop ()) ;
          double d3 = 0 ;
          if ("+".equals (a)) {
            d3 = d1 + d2 ;
          } else if ("-".equals (a)) {
            d3 = d1 - d2 ;
          } else if ("*".equals (a)) {
            d3 = d1 * d2 ;
          } else if ("/".equals (a)) {
            d3 = d1 / d2 ;
          }
          calStack.push (String.valueOf (d3)) ;
        } else {
          calStack.push (a) ;
        }
      }
      return doValues (map , calStack.pop ()) ;
    }
 
  private static double doValues ( Map<String,Double> map , String columnName ){
    //System.out.println(columnName);
    columnName = columnName.replaceAll ("'" , "") ;
    columnName = columnName.replaceAll ("/"" , "") ;
    boolean isColumnName = false;
    if ( columnName.startsWith("$")){
      isColumnName = true;
      columnName = columnName.substring(1);
    }
    if ( isColumnName ){
      try{
        return map.get (columnName) ;
      }catch ( Exception sysEx ){
        return 0;
      }
    }else{
      try {
        return Double.parseDouble (columnName) ;
      } catch (NumberFormatException e) {
        try {
          return map.get (columnName) ;
        } catch (Exception sysEx) {
          return 0 ;
        }
      }
    }
  }

 

对二维数据进行求值的算法

public static double tableCalculate (List<Map<String,Double>> ds , Stack<String> sourceStack) {
    Stack<String> callStack = new Stack<String> () ;
    @SuppressWarnings("unchecked")
    Stack<String> cloneStack = (Stack<String>)sourceStack.clone(); //保证之前的栈不变.
    while (!cloneStack.empty ()) {
      String a = cloneStack.pop () ;
      //System.out.println (a) ;
      if (isOperator (a)) {
        double d2 = tableFunValue (ds , callStack.pop ()) ;
        double d1 = tableFunValue (ds , callStack.pop ()) ;
        double d3 = 0 ;
        if ("+".equals (a)) {
          d3 = d1 + d2 ;
        } else if ("-".equals (a)) {
          d3 = d1 - d2 ;
        } else if ("*".equals (a)) {
          d3 = d1 * d2 ;
        } else if ("/".equals (a)) {
          d3 = d1 / d2 ;
        }
        callStack.push (String.valueOf (d3)) ;
      } else {
        callStack.push (a) ;
      }
    }
    return tableFunValue (ds , callStack.pop ()) ;
  }
 
  private static double tableFunValue (List<Map<String,Double>> ds , String fun) {
    try {
      fun = fun.trim () ;
      fun = fun.replaceAll (" " , "") ;
      fun = fun.replaceAll ("/t" , "") ;
      fun = fun.toLowerCase () ;
      String cn ;
      if (fun.startsWith ("sum")) {
        cn = fun ;
        cn = cn.substring (3) ;
        Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;
        double sum = 0 ;
        for (int i = 0 ; i < ds.size() ; i++) {
          sum += Expression.rowCalculate (ds.get (i) , st) ;
        }
        return sum ;
      } else if (fun.startsWith ("avg")) {
        cn = fun ;
        cn = cn.substring (3) ;
        Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;
        double sum = 0 ;
        for (int i = 0 ; i < ds.size () ; i++) {
          sum += Expression.rowCalculate (ds.get (i) , st) ;
        }
        return sum / ds.size () ;
      } else if (fun.startsWith ("min")) {
        cn = fun ;
        cn = cn.substring (3) ;
        Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;
        double min = Double.MAX_VALUE ;
        for (int i = 0 ; i < ds.size () ; i++) {
          double tmin = Expression.rowCalculate (ds.get (i) , st) ;
          if (tmin < min) {
            min = tmin ;
          }
        }
        return min;
      } else if (fun.startsWith ("max")) {
        cn = fun ;
        cn = cn.substring (3) ;
        Stack<String> st = Expression.convertToPostfix (new FunctionSplit (cn)) ;
        double max = Double.MIN_VALUE ;
        for (int i = 0 ; i < ds.size () ; i++) {
          double tmax = Expression.rowCalculate (ds.get (i) , st) ;
          if (tmax > max) {
            max = tmax ;
          }
        }
        return max ;
      } else if (fun.startsWith ("rowcount")) {
        return ds.size () ;
      } else {
        return Double.parseDouble (fun) ;
      }
    } catch (Exception ex) {
      ex.printStackTrace () ;
      return 0 ;
    }
  }

 

 

五、其他辅助函数

/**
   * 字符串是运算符号.
   * @param s String
   * @return boolean
   */
  public static boolean isOperator (String s) {
    if ("+".equals (s) || "-".equals (s) || "*".equals (s) || "/".equals (s)) {
      return true ;
    } else {
      return false ;
    }
  }
 
  /**
   * 字符是运算符号.
   */
  public static boolean isOperator (char s) {
    if ('+' == s || '-' == s || '*' == s || '/' == s) {
      return true ;
    } else {
      return false ;
    }
  }
 
  public static boolean isOpenParenthesis (String s) {
    return "(".equals (s) ;
  }
 
  public static boolean isOpenParenthesis (char s) {
    return '(' == s ;
  }
 
  public static boolean isCloseParenthesis (String s) {
    return ")".equals (s) ;
  }
 
  public static boolean isCloseParenthesis (char s) {
    return ')' == s ;
  }
 
  public static boolean isBlank (String s) {
    if (" ".equals (s) || "/t".equals (s)) {
      return true ;
    } else {
      return false ;
    }
  }
 
  public static boolean isBlank (char s) {
    if (' ' == s || '/t' == s) {
      return true ;
    } else {
      return false ;
    }
  }
 
  public static boolean isDot (char s) {
    return s == '.' ;
  }
 
  static int priority (String s) {
    if (s == null) {
      return -1 ;
    }
    char c = s.charAt (0) ;
    if (c == '^') {
      return 3 ; /*Exponential operator*/
    }
    if (c == '*' || c == '/' || c == '%') {
      return 2 ;
    } else if (c == '+' || c == '-') {
      return 1 ;
    } else {
      return 0 ;
    }
  }

 

六、小结

实际上在实际应用中也会有如下的应用场景。

求每一行数据和平均数据平均值的偏差。次问题,可以在方法tableCalculate的基础上在进行适当的扩充即可。

List<Map<String, Double>> dataTable = null;
           StringSplit split = new FunctionSplit(
                  "(avg(price)-price)/avg(price)*100.0");
           Stack<String> postFixStack = Expression.convertToPostfix(split);
           for (int rowNumber=0;rowNumber<dataTable.size();rowNumber++) {
              Map<String,Double> row = dataTable.get(rowNumber);
              System.out.println("本月销售价格:"+row.get("price"));
              System.out.println("本月销售价格偏差"
                     + Expression.tableRowCalculate(dataTable, rowNumber,
                            postFixStack));
           }