java數學表達式計算 QLExpress

fmms 12年前發布 | 28K 次閱讀 Java開發 Java

 你想知道   a love b = ? 嗎 ,你想隨意定義自己的操作符號嗎 ? 請使用 QLExpress工具包
 這個表達式相對別的計算工具,優點主要體現在:
      A、不需要預先加載可能需要的所有屬性值
      B、 用戶可以根據業務需要自定義操作符號和函數
      C、可以同步輸出判斷錯誤信息,有利于提高業務系統在規則判斷等使用場景下的用戶體驗。減少業務系統相關的處理代碼。
    
       主要用途:一些業務規則的組合判斷,同時需要輸出相關的錯誤信息
  
   執行的流程:
   1、單詞分解
   2、語法分析
 
  最簡單Hello范例:
 
   String express = "10 * 10 + 1 + 2 * 3 + 5 * 2";
   ExpressRunner runner = new ExpressRunner();
   Object result = runner.execute(express, null, false, null);
   System.out.println("表達式計算:" + express + " = " + result);
 
  其它范例:
   ExpressRunner runner = new ExpressRunner();
  runner.addOperator("love", new LoveOperator("love"));
  runner.addOperatorWithAlias("屬于", "in", "用戶$1不在允許的范圍");
  runner.addOperatorWithAlias("myand", "and", "用戶$1不在允許的范圍");
  runner.addFunction("累加", new GroupOperator("累加"));
  runner.addFunction("group", new GroupOperator("group"));
  runner.addFunctionOfClassMethod("isVIP", BeanExample.class.getName(),
    "isVIP", new String[] { "String" }, "$1不是VIP用戶");
  runner.addFunctionOfClassMethod("取絕對值", Math.class.getName(), "abs",
    new String[] { "double" }, null);
  runner.addFunctionOfClassMethod("轉換為大寫", BeanExample.class.getName(),
    "upper", new String[] { "String" }, null); 

      在這個計算引擎里面, 執行下述表達式結果:
       Example 0 : System.out.println("ss") =  null
       Example 1 : unionName = new com.ql.util.express.test.BeanExample("張三").unionName("李四") =  張三-李四
       Example 2 : group(2,3,4) =  9
       Example 3 : 取絕對值(-5.0) =  5.0
       Example 4 : max(2,3,4,10) =  10
       Example 5 :  max(3,2) + 轉換為大寫("abc") =  3ABC
       Example 6 :  c = 1000 + 2000 =  3000
       Example 7 : b = 累加(1,2,3)+累加(4,5,6) =  21
       Example 8 : 三星賣家 and 消保用戶  =  true
       Example 9 : 'a' love 'b' love 'c' love 'd' =  d{c{b{a}b}c}d
       Example 10 :  10 * 10 + 1 + 2 * 3 + 5 * 2 =  117
       Example 11 :  ( (1+1) 屬于 (4,3,5)) and isVIP("玄難") =  false
         系統輸出的錯誤提示信息:[用戶 2 不在允許的范圍,  玄難 不是VIP用戶]
      
 
  表達式支持概述:
  1、基本的java語法:
   A、四則運算 : 10 * 10 + 1 + 2 * 3 + 5 * 2
  B、boolean運算: 3 > 2 and 2 > 3
     C、創建對象,對象方法調用,靜態方法調用:new com.ql.util.express.test.BeanExample("張三").unionName("李四")
     D、變量賦值:a = 3 + 5
     F、支持 in,max,min:  (a in (1,2,4)) and (b in("abc","bcd","efg"))
  2、提供表達式上下文,屬性的值不需要在初始的時候全部加入,
     而是在表達式計算的時候,需要什么信息才通過上下文接口獲取
     避免因為不知道計算的需求,而在上下文中把可能需要的數據都加入。
     runner.execute("三星賣家 and 消保用戶",errorList,true,expressContext)
     "三星賣家"和"消保用戶"的屬性是在需要的時候通過接口去獲取。
  3、可以將計算結果直接存儲到上下文中供后續業務使用。例如:
        runner.execute("c = 1000 + 2000",errorList,true,expressContext);
        則在expressContext中會增加一個屬性c=3000,也可以在expressContext實現直接的數據庫操作等。
  4、 可以將類和Spring對象的方法映射為表達式計算中的別名,方便其他業務人員的立即和配置。例如:
     將 Math.abs() 映射為 "取絕對值"。 則  "取絕對值(-5.0)" = "5.0"
     runner.addFunctionOfClassMethod("取絕對值", Math.class.getName(), "abs",new String[] { "double" }, null);
  5、可以為已經存在的boolean運算操作符號設置別名,增加錯誤信息同步輸出,在計算結果為false的時候,同時返回錯誤信息。例如:
     runner.addOperatorWithAlias("屬于", "in", "用戶$1不在允許的范圍")。
     用戶自定義的函數同樣也可以設置錯誤信息:例如:
   runner.addFunctionOfClassMethod("isOk", BeanExample.class.getName(),"isOk", new String[] { "String" }, "$1 不是VIP用戶");
 
     則在調用List errorList = new ArrayList();
     Object result =runner.execute("( (1+1) 屬于 (4,3,5)) and isOk("玄難")",errorList,true,null);
      執行結果 result = false.同時在errorList中還會返回2個錯誤原因:
       1、"用戶 2 不在允許的范圍",2、玄難 不是VIP用戶  
     這在業務系統需要進行規則計算,同時需要返回
   
  6、可以自定義計算函數。例如:
     自定一個操作函數 group:
 class GroupOperator extends Operator {
 public GroupOperator(String aName) {
  this.name= aName;
 }
 public Object executeInner(Object[] list)throws Exception {
  Object result = new Integer(0);
  for (int i = 0; i < list.length; i++) {
   result = OperatorOfNumber.Add.execute(result, list[i]);
  }
  return result;
 }
 }
          然后增加到運算引擎:
     runner.addFunction("累加", new GroupOperator("累加"));
  runner.addFunction("group", new GroupOperator("group"));
          則 執行:group(2,3,4)  = 9 ,累加(1,2,3)+累加(4,5,6)=21
  7、可以自定義新的操作符號  。自定義的操作符號優先級設置為最高。例如 :
    自定一個操作函數 love:
class LoveOperator extends Operator { 
 public LoveOperator(String aName) {
  this.name= aName;
 }
 public Object executeInner(Object[] list)
   throws Exception {
  String op1 = list[0].toString();
  String op2 = list[1].toString();
  String result = op2 +"{" + op1 + "}" + op2;  
  return result;
 }
}
         然后增加到運算引擎:
      runner.addOperator("love", new LoveOperator("love"));
        則 執行:'a' love 'b' love 'c' love 'd' = "d{c{b{a}b}c}d"
  8、運算引擎在沒有預編譯的情況下, 執行10萬次  "10 * 10 + 1 + 2 * 3 + 5 * 2" 耗時:3.187秒
             runner.execute("10 * 10 + 1 + 2 * 3 + 5 * 2", null, false,null);
            在打開 預編譯緩存開關的情況下, 執行10萬次 "10 * 10 + 1 + 2 * 3 + 5 * 2" 耗時:  0.171秒
             runner.execute("10 * 10 + 1 + 2 * 3 + 5 * 2", null, true,null);      
            運行引擎是線程安全的。在業務系統中實際使用過程中應該打開緩存預編譯的開關,性能會更加。
             可以調用clearExpressCache()清除緩存 。
           在開啟開關的情況下,會緩存解析后的最后執行指令如下所示,
           避免了字符串解析、詞法分析、語法分析等步驟,簡單對比一下,會提高30倍的速度:
 1:LoadData 10
  2:LoadData 10
  3:OP : * OPNUMBER[2]
  4:LoadData 1
  5:OP : + OPNUMBER[2]
  6:LoadData 2
  7:LoadData 3
  8:OP : * OPNUMBER[2]
  9:OP : + OPNUMBER[2]
  10:LoadData 5
  11:LoadData 2
  12:OP : * OPNUMBER[2]
  13:OP : + OPNUMBER[2]    
   9、這個表達式相對別的計算工具,有點主要體現在:
      A、不需要預先加載可能需要的所有屬性值
      B、 用戶可以根據業務需要自定義操作符號和函數
      C、可以同步輸出判斷錯誤信息,有利于提高業務系統在規則判斷等使用場景下的用戶體驗。減少業務系統相關的處理代碼。
   10、后續可以進一步優化的地方:
      A、現有的單詞拆解、詞法分析、語法分析都是自己寫的山寨版,可以利用其他成熟的開源工具。
      B、優化具體的操作指令,提高單個操作符號的運行效率
      C、經過簡單的擴展支持自定義代碼片段的運行。  

項目主頁:http://www.baiduhome.net/lib/view/home/1326886081405

 本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!