整体思路保持不变,先对表达式分析,将其转化为若干运算单元(运算单元表示一个广义运算符或者一个操作数),然后根据运算单元的列表对表达式求值。

变化点

一元运算符的处理

先前的简易计算器表示运算的运算符只有加减乘除、正负号,正负号虽然是一元运算符,但做了特殊处理(直接将紧跟其后的操作数的正负性做出相应的改变,当引入高于正负号优先级的乘方阶乘后就变为了错误的做法)。
科学计算器新引入了阶乘、sin、cos、tan、ln、log、sqrt这些一元运算符,也要求能够更好的处理一元运算符。

运算符数量大大增加

运算符数量增加,将会加大修改运算符优先级比较矩阵的难度,如果每新增支持一种新的运算就去修改这个矩阵,显然过于繁琐,因此采用了一种基于规则的比较方式。

代码量增加

随着代码量的增加,需要对项目重构。

项目结构

主要代码位于cn.zhikaizhang.algorithm和cn.zhikaizhang.main两个包下。前者是算法,后者是界面与交互。

cn.zhikaizhang.algorithmCalculator表达式求值
cn.zhikaizhang.algorithmExpressionIllegalException表达式不合法异常类
cn.zhikaizhang.algorithmExpressionParser表达式分析器,将表达式转化为运算单元列表
cn.zhikaizhang.algorithmOperation运算
cn.zhikaizhang.algorithmOperatorComparator运算符优先级比较器
cn.zhikaizhang.algorithmUnit运算单元,使用一个枚举型变量标识操作数以及每种运算符
cn.zhikaizhang.mainFxCalcApplication继承自Application,fx-Calc启动类
cn.zhikaizhang.mainFxCalcLookAndFeel控制fx-Calc的外观
cn.zhikaizhang.mainFxCalcMainController交互,界面上按钮的点击事件

运行效果

上面的有些运算是基于其他的基本运算的,如倒数利用乘方的基本运算,EE(1.0E3表示1.0乘10的三次方)使用乘法和乘方。

实现细节

Unit类

运算单元(操作数或广义的运算符),将可运算的运算符、左右括号、起止符并称为广义运算符。
type变量标识着一个运算单元的类型。可运算的运算符使用priority表示优先级,操作数使用val表示值。

可运算运算符对传入的操作数进行运算。不合法抛异常。通过可变长参数列表可以对一元和二元运算符作统一的处理。

可运算的运算符的优先级设置。优先级设置为6的运算符作为函数进行特殊处理,后面必须紧跟着括号,由ExpressionParser处理,因此不会与其他运算冲突。

OperatorComparator类

基于规则比较优先级,isOperator()表示是否广义运算符,isNormalOperator()表示是否可运算运算符,分别考虑两个都是可运算运算符以及存在左右括号、起止符的情形。
如果两个都是可运算运算符,只要前者的优先级大于等于后者的优先级,就认为前者大,唯一一个例外是前者是乘方符号,后者是负号,虽然乘方的priority大于负号,此时应该先计算负号,因此返回小于。

Operation类

此类中实现了所有的基本运算,为了避免double类型运算时的精度损失,如果结果不是无穷或者NaN就使用BigDecimal类型进行计算,以加法为例。

github

github项目主页: Calculator

01-14 19:21
查看更多