java数学表达式计算 QLExpress

fmms 13年前
     <p> 你想知道   a love b = ? 吗 ,你想随意定义自己的操作符号吗 ? 请使用 QLExpress工具包<br />  这个表达式相对别的计算工具,优点主要体现在:<br />       A、不需要预先加载可能需要的所有属性值<br />       B、 用户可以根据业务需要自定义操作符号和函数 <br />       C、可以同步输出判断错误信息,有利于提高业务系统在规则判断等使用场景下的用户体验。减少业务系统相关的处理代码。<br />      <br />        主要用途:一些业务规则的组合判断,同时需要输出相关的错误信息<br />    <br />    执行的流程:<br />    1、单词分解<br />    2、语法分析<br />  <br />   最简单Hello范例:<br />   <br />    String express = "10 * 10 + 1 + 2 * 3 + 5 * 2";<br />    ExpressRunner runner = new ExpressRunner();<br />    Object result = runner.execute(express, null, false, null);<br />    System.out.println("表达式计算:" + express + " = " + result);<br />  <br />   其它范例:<br />    ExpressRunner runner = new ExpressRunner();<br />   runner.addOperator("love", new LoveOperator("love"));<br />   runner.addOperatorWithAlias("属于", "in", "用户$1不在允许的范围");<br />   runner.addOperatorWithAlias("myand", "and", "用户$1不在允许的范围");<br />   runner.addFunction("累加", new GroupOperator("累加"));<br />   runner.addFunction("group", new GroupOperator("group"));<br />   runner.addFunctionOfClassMethod("isVIP", BeanExample.class.getName(),<br />     "isVIP", new String[] { "String" }, "$1不是VIP用户");<br />   runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs",<br />     new String[] { "double" }, null);<br />   runner.addFunctionOfClassMethod("转换为大写", BeanExample.class.getName(),<br />     "upper", new String[] { "String" }, null); </p>    <p>      在这个计算引擎里面, 执行下述表达式结果:<br />        Example 0 : System.out.println("ss") =  null<br />        Example 1 : unionName = new com.ql.util.express.test.BeanExample("张三").unionName("李四") =  张三-李四<br />        Example 2 : group(2,3,4) =  9<br />        Example 3 : 取绝对值(-5.0) =  5.0<br />        Example 4 : max(2,3,4,10) =  10<br />        Example 5 :  max(3,2) + 转换为大写("abc") =  3ABC<br />        Example 6 :  c = 1000 + 2000 =  3000<br />        Example 7 : b = 累加(1,2,3)+累加(4,5,6) =  21<br />        Example 8 : 三星卖家 and 消保用户  =  true<br />        Example 9 : 'a' love 'b' love 'c' love 'd' =  d{c{b{a}b}c}d<br />        Example 10 :  10 * 10 + 1 + 2 * 3 + 5 * 2 =  117<br />        Example 11 :  ( (1+1) 属于 (4,3,5)) and isVIP("玄难") =  false<br />          系统输出的错误提示信息:[用户 2 不在允许的范围,  玄难 不是VIP用户]<br />        <br />   <br />   表达式支持概述:<br />   1、基本的java语法:<br />    A、四则运算 : 10 * 10 + 1 + 2 * 3 + 5 * 2<br />   B、boolean运算: 3 > 2 and 2 > 3<br />      C、创建对象,对象方法调用,静态方法调用:new com.ql.util.express.test.BeanExample("张三").unionName("李四")<br />      D、变量赋值:a = 3 + 5<br />      F、支持 in,max,min:  (a in (1,2,4)) and (b in("abc","bcd","efg"))<br />   2、提供表达式上下文,属性的值不需要在初始的时候全部加入,<br />      而是在表达式计算的时候,需要什么信息才通过上下文接口获取 <br />      避免因为不知道计算的需求,而在上下文中把可能需要的数据都加入。<br />      runner.execute("三星卖家 and 消保用户",errorList,true,expressContext)<br />      "三星卖家"和"消保用户"的属性是在需要的时候通过接口去获取。<br />   3、可以将计算结果直接存储到上下文中供后续业务使用。例如:<br />         runner.execute("c = 1000 + 2000",errorList,true,expressContext);<br />         则在expressContext中会增加一个属性c=3000,也可以在expressContext实现直接的数据库操作等。<br />   4、 可以将类和Spring对象的方法映射为表达式计算中的别名,方便其他业务人员的立即和配置。例如:<br />      将 Math.abs() 映射为 "取绝对值"。 则  "取绝对值(-5.0)" = "5.0"<br />      runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs",new String[] { "double" }, null);<br />   5、可以为已经存在的boolean运算操作符号设置别名,增加错误信息同步输出,在计算结果为false的时候,同时返回错误信息。例如:<br />      runner.addOperatorWithAlias("属于", "in", "用户$1不在允许的范围")。<br />      用户自定义的函数同样也可以设置错误信息:例如:<br />    runner.addFunctionOfClassMethod("isOk", BeanExample.class.getName(),"isOk", new String[] { "String" }, "$1 不是VIP用户");<br />   <br />      则在调用List errorList = new ArrayList();<br />      Object result =runner.execute("( (1+1) 属于 (4,3,5)) and isOk("玄难")",errorList,true,null);<br />       执行结果 result = false.同时在errorList中还会返回2个错误原因:<br />        1、"用户 2 不在允许的范围",2、玄难 不是VIP用户   <br />      这在业务系统需要进行规则计算,同时需要返回<br />     <br />   6、可以自定义计算函数。例如:<br />      自定一个操作函数 group:<br />  class GroupOperator extends Operator {<br />  public GroupOperator(String aName) {<br />   this.name= aName;<br />  }<br />  public Object executeInner(Object[] list)throws Exception {<br />   Object result = new Integer(0);<br />   for (int i = 0; i < list.length; i++) {<br />    result = OperatorOfNumber.Add.execute(result, list[i]);<br />   }<br />   return result;<br />  }<br />  }<br />           然后增加到运算引擎:<br />      runner.addFunction("累加", new GroupOperator("累加"));<br />   runner.addFunction("group", new GroupOperator("group"));<br />           则 执行:group(2,3,4)  = 9 ,累加(1,2,3)+累加(4,5,6)=21<br />   7、可以自定义新的操作符号  。自定义的操作符号优先级设置为最高。例如 :<br />     自定一个操作函数 love:<br /> class LoveOperator extends Operator { <br />  public LoveOperator(String aName) {<br />   this.name= aName;<br />  }<br />  public Object executeInner(Object[] list)<br />    throws Exception {<br />   String op1 = list[0].toString();<br />   String op2 = list[1].toString();<br />   String result = op2 +"{" + op1 + "}" + op2;  <br />   return result;<br />  }<br /> }<br />          然后增加到运算引擎:<br />       runner.addOperator("love", new LoveOperator("love"));<br />         则 执行:'a' love 'b' love 'c' love 'd' = "d{c{b{a}b}c}d"<br />   8、运算引擎在没有预编译的情况下, 执行10万次  "10 * 10 + 1 + 2 * 3 + 5 * 2" 耗时:3.187秒<br />              runner.execute("10 * 10 + 1 + 2 * 3 + 5 * 2", null, false,null);<br />             在打开 预编译缓存开关的情况下, 执行10万次 "10 * 10 + 1 + 2 * 3 + 5 * 2" 耗时:  0.171秒<br />              runner.execute("10 * 10 + 1 + 2 * 3 + 5 * 2", null, true,null);       <br />             运行引擎是线程安全的。在业务系统中实际使用过程中应该打开缓存预编译的开关,性能会更加。<br />              可以调用clearExpressCache()清除缓存 。<br />            在开启开关的情况下,会缓存解析后的最后执行指令如下所示,<br />            避免了字符串解析、词法分析、语法分析等步骤,简单对比一下,会提高30倍的速度:<br />  1:LoadData 10<br />   2:LoadData 10<br />   3:OP : * OPNUMBER[2] <br />   4:LoadData 1<br />   5:OP : + OPNUMBER[2] <br />   6:LoadData 2<br />   7:LoadData 3<br />   8:OP : * OPNUMBER[2] <br />   9:OP : + OPNUMBER[2] <br />   10:LoadData 5<br />   11:LoadData 2<br />   12:OP : * OPNUMBER[2] <br />   13:OP : + OPNUMBER[2]     <br />    9、这个表达式相对别的计算工具,有点主要体现在:<br />       A、不需要预先加载可能需要的所有属性值<br />       B、 用户可以根据业务需要自定义操作符号和函数 <br />       C、可以同步输出判断错误信息,有利于提高业务系统在规则判断等使用场景下的用户体验。减少业务系统相关的处理代码。<br />    10、后续可以进一步优化的地方:<br />       A、现有的单词拆解、词法分析、语法分析都是自己写的山寨版,可以利用其他成熟的开源工具。<br />       B、优化具体的操作指令,提高单个操作符号的运行效率<br />       C、经过简单的扩展支持自定义代码片段的运行。   <br /> <br /> </p>    <p><strong>项目主页:</strong><a href="http://www.open-open.com/lib/view/home/1326886081405" target="_blank">http://www.open-open.com/lib/view/home/1326886081405</a></p>