整体思路保持不变,先对表达式分析,将其转化为若干运算单元(运算单元表示一个广义运算符或者一个操作数),然后根据运算单元的列表对表达式求值。
变化点
一元运算符的处理
先前的简易计算器表示运算的运算符只有加减乘除、正负号,正负号虽然是一元运算符,但做了特殊处理(直接将紧跟其后的操作数的正负性做出相应的改变,当引入高于正负号优先级的乘方阶乘后就变为了错误的做法)。
科学计算器新引入了阶乘、sin、cos、tan、ln、log、sqrt这些一元运算符,也要求能够更好的处理一元运算符。
运算符数量大大增加
运算符数量增加,将会加大修改运算符优先级比较矩阵的难度,如果每新增支持一种新的运算就去修改这个矩阵,显然过于繁琐,因此采用了一种基于规则的比较方式。
代码量增加
随着代码量的增加,需要对项目重构。
项目结构
主要代码位于cn.zhikaizhang.algorithm和cn.zhikaizhang.main两个包下。前者是算法,后者是界面与交互。
cn.zhikaizhang.algorithm | Calculator | 表达式求值 |
cn.zhikaizhang.algorithm | ExpressionIllegalException | 表达式不合法异常类 |
cn.zhikaizhang.algorithm | ExpressionParser | 表达式分析器,将表达式转化为运算单元列表 |
cn.zhikaizhang.algorithm | Operation | 运算 |
cn.zhikaizhang.algorithm | OperatorComparator | 运算符优先级比较器 |
cn.zhikaizhang.algorithm | Unit | 运算单元,使用一个枚举型变量标识操作数以及每种运算符 |
cn.zhikaizhang.main | FxCalcApplication | 继承自Application,fx-Calc启动类 |
cn.zhikaizhang.main | FxCalcLookAndFeel | 控制fx-Calc的外观 |
cn.zhikaizhang.main | FxCalcMainController | 交互,界面上按钮的点击事件 |
运行效果
上面的有些运算是基于其他的基本运算的,如倒数利用乘方的基本运算,EE(1.0E3表示1.0乘10的三次方)使用乘法和乘方。
实现细节
Unit类
运算单元(操作数或广义的运算符),将可运算的运算符、左右括号、起止符并称为广义运算符。
type变量标识着一个运算单元的类型。可运算的运算符使用priority表示优先级,操作数使用val表示值。
可运算运算符对传入的操作数进行运算。不合法抛异常。通过可变长参数列表可以对一元和二元运算符作统一的处理。
可运算的运算符的优先级设置。优先级设置为6的运算符作为函数进行特殊处理,后面必须紧跟着括号,由ExpressionParser处理,因此不会与其他运算冲突。
OperatorComparator类
基于规则比较优先级,isOperator()表示是否广义运算符,isNormalOperator()表示是否可运算运算符,分别考虑两个都是可运算运算符以及存在左右括号、起止符的情形。
如果两个都是可运算运算符,只要前者的优先级大于等于后者的优先级,就认为前者大,唯一一个例外是前者是乘方符号,后者是负号,虽然乘方的priority大于负号,此时应该先计算负号,因此返回小于。
Operation类
此类中实现了所有的基本运算,为了避免double类型运算时的精度损失,如果结果不是无穷或者NaN就使用BigDecimal类型进行计算,以加法为例。
github
github项目主页: Calculator