基于逆波兰表达式的公式解析器-算法和思路
Posted yutingliuyl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于逆波兰表达式的公式解析器-算法和思路相关的知识,希望对你有一定的参考价值。
背景:
近期项目须要自己完毕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:转载请注明出处。
以上是关于基于逆波兰表达式的公式解析器-算法和思路的主要内容,如果未能解决你的问题,请参考以下文章