zoukankan      html  css  js  c++  java
  • 基于逆波兰表达式的公式解析器-算法和思路(一)

    背景:

           近期项目须要自己完毕Excel的公式解析和求值,在Java中能够使用POI解析Excel公式然后求值。可是项目须要JS端和Java后端均须要支持公式解析,所以就须要自己写一套了。事实上公式解析器整体上并不复杂。原理使用逆波兰表达式就可了。

    难点:

           1. 针对复杂的用户输入环境解析公式,须要注意公式书写不规范、大写和小写、空格等问题,甚至公式出错的推断。

           2. 须要解决函数扩展、函数运行等问题。

           3. 须要解决地址、地址范围取数,求值问题。

           4. 处理括号带来的优先级提升。

           5. 解决公式嵌套求知问题。

           6. 財务小数精度,解决採用IEEE 754标准出现的0.3 –0.2 != 0.1问题。

           7. 解决循环引用问题,也就是公式链成环问题。

     

    理论和原理:

           1. 基于逆波兰表达式。将用户输入解析为后缀表达式。

           2. 抽象操作数和操作符,操作数(operand)操作符(operator)分别放入两个stack。

           3. 将函数抽象为operator。地址、地址范围抽象为operand。

     

           关于逆波兰表达式,在数据结构和编译原理中都已经讲烂了。简介下:

           逆波兰表达式是波兰逻辑学家在1929年提出的一种表达式表达方式。

    我们传统的表达式操作符一般都是在两个数之间的(先仅限于二元操作符)。而逆波兰表达式的操作符在数字的后面。看以下一个简单样例就知道了:

    转换前:1 + 2 – 3 * 4 
    转换后:1 , 2 , + , 3 , 4 , * , -

           长处:逆波兰表达式很适合机器运行。能屏蔽掉括号对运算符的优先级的提升。

     

    一般算法:

           逆波兰表达式的一般解析算法是建立在简单算术表达式上的,它是我们进行公式解析和运行的基础:

           1. 构建两个栈Operand(操作数栈)和Operator(操作符栈)。

           2.扫描给定的字符串,假设得到一个数字,则提取(扫描是一位一位的,一定要提取一个完整的数字)数字(下面用Operand取代),然后把Operand压入Operand栈中。

           3. 假设获得一个运算符(比方+或者*,以下用B取代),则须要和Operator栈栈顶元素(用A替代)比較:

                  1) 假设A不存在,则把B压入Operator栈中。

                  2)假设B是一个左括号,则忽略A和B的优先级比較,把B压入Operator栈。

                  3)假设B是一个右括号。则把Operator栈顺序出栈,然后把弹出的元素顺序压入Operand栈中,直到栈顶弹出的是左括号,括号不入Operand栈中。

                  4)假设A是左括号。则把B直接压入Operator栈。

                  5)假设B优先级比較A高。则把B直接压入Operator栈。

                  6)假设B优先级低于或等于A的优先级。则把A出栈然后压入Operand栈,重复进行此步骤直到栈顶优先级高于B的优先级或者栈顶是一个括号。

           4.扫描完成后。把Operator栈的元素依次出栈,然后依次压入Operand栈中。

     

    算法特点:

           1.使用两个stack,用来构建后缀表达式,Operator栈忽略括号的情况下,始终是高优先级的Operator在栈顶。

           2.括号的优先级最低,优先级优自定义。

           3.到最后我们仅仅剩下一个Operator栈,从栈低依次运算就可以。

           4.优先级比較是关键,优先级关系到出入栈顺序和终于结果。

     

    公式解析的复杂性:

           相比传统的算术表达式的解析,Excel类公式的解析更加复杂:

           1. 须要支持的操作数和操作符众多:不不过数字操作数和数学操作符,还会包括比較运算符、函数、逻辑操作数。字符串操作数,地址(如Excel的A1,A2)等。

           2. 公式的组成比較复杂。扫描困难:扫描时须要提取数字(有可能是科学计数法)、单词(比方函数、地址、地址范围),还可能出现一元操作符(比方取非!。预防需求的变化,需求是最坑程序猿的)。

           3. 同一操作符代表的含义不同:操作符重载情况比較到。比方“-”。就可以能是“减号”,也可能是“符号”;再比方运行函数功能时。函数须要多少个操作数。地址范围(A1:A10)怎样运行求值。

           4. 计算精度,这是最麻烦的(Java和JS的那个坑爹的0.1问题),既要保证效率,又要保证精度(这个世界不公平啊)。


    总结:

           使用逆波兰表达式解析公式,我们须要对整改算法做小小的修改。将操作数和操作符的范围提升,不只局限在算术表达式的范围内。

    这就要求我们解析的时候书写正則表達式(或词法分析的办法)来提取完整的操作数、操作符,甚至依据上下文环境对操作符进行重载(比方-究竟是减法还是符号)。今天到这儿,明天说下改进算法和代码。



    PS:转载请注明出处。

    大哭


  • 相关阅读:
    Poj 2391 二分答案+最大流+拆点
    POJ 1087 A Plug for UNIX 最大流
    POJ 1459 Power Network 最大流
    POJ 2112 Optimal Milking 二分答案+最大流
    POJ 1273 Drainage Ditches 最大流
    POJ 1149 PIGS 最大流
    POJ 2288 Islands and Bridges 哈密尔顿路 状态压缩DP
    The Cow Lexicon
    1523. K-inversions
    1350. Canteen
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/7141222.html
Copyright © 2011-2022 走看看