Swift后缀表达式(逆波兰式)转换计算
背景
最近在开发《挑战24点》的过程中遇到了一个问题,即,如何计算常用数学表达式的结果,即,给定字符串8 - (6 + 4 / 2 - 1) * 2
,怎么计算得到结果,并且得到计算的过程。
网上查资料发现,大部分都是类似系统计算器的处理,在遇到第二个运算符时,就把前一步的操作结果计算出来。这样的处理方式并不适用于笔者想要解决的问题。
进一步搜索后发现,前缀表达式、中缀表达式、后缀表达式的概念,给定的字符串8 - (6 + 4 / 2 - 1) * 2
属于中缀表达式,而想要计算机得出结果,可以转为前缀表达式或者后缀表达式,然后再对转换后的表达式进行计算。
这里采用中缀表达式转后缀表达式,然后计算后缀表达式得出结果,步骤如下。
Swift 中缀表达式转后缀表达式
什么是中缀表达式、后缀表达式?
- 中缀表达式: 是常用的算术表示方法,操作符处于操作数的中间,比如 (a + b),即中缀形式,故而称之为中缀表达式。
- 后缀表达式: 运算符写在操作数之后,比如 (a, b, +),称之为后缀表达式,又名逆波兰式。
为什么要把中缀表达式转为后缀表达式?
为什么要将简单的中缀表达式转为后缀表达式呢?因为中缀表达式的简单对于计算机来说是非常复杂的,没有办法直接运算,而后缀表达式对于计算机而言是简单易懂的结构。所以计算常见的表达式时,要转为后缀表达式,然后运算。
怎么转?
然后来看下,如何把中缀表达式转为后缀表达式,这里建议先看一遍,理解后,在本子上按照原理尝试一遍,更能理解。
原理:
- 由于 Swift 中没有栈的概念,所以采用数组的实现方式,用数组 append 模拟入栈,popLast 来模拟出栈。
- 声明两个数组,分别用于存储数字和运算符
- 从左到右遍历表达式,
- 遇到” “,继续遍历下一个字符
- 遇到数字则放入数字数组中
- 遇到”)”,则把运算符数组中最后一个元素弹出,直到遇到”(“时停止。
- 遇到运算符,则比较运算符的优先级,
- 运算符数组中最后一个为”(“时,或者要放入的运算符为”(“,则不需要比较优先级,直接把要放入的运算符放入运算符数组中
- 如果要放入的运算符的优先级不大于运算符数组中最后一个的优先级,则把运算符数组中的最后一个弹出放入到数字数组中,直到遇到优先级比它低的时停止或者遇到”(“时停止。然后把运算符放入运算符数组中。
- 遍历表达式完成后,如果运算符数组不为空,则把运算符数组中的元素倒序弹出,放入到数字数组中
- 最后返回数字数组,即所需要的后缀表达式的数组
流程如下:
假设现有一个表达式:8 - (6 + 4 / 2 - 1) * 2
,按照上面的步骤实践如下:
1 |
|
这个地方可以多理解几次,里面的逻辑按步骤划分之后,再来写实现代码,代码实现如下:
1 |
|
后缀表达式的计算
后缀表达式计算的原理
后缀表达式计算的原理如下:
从左到右遍历数组,遇到运算符后,把运算符 op 前面的两个数字a, b取出,按照 a op b 的逻辑计算,并把三个元素从数组中移除。(这里需要注意移除时的方法,不能一个个移除,移除一个后,数组元素的位置就发生了改变)
将 运算结果r 插入到数组中计算前 a 的位置
重复遍历数组,按照上面逻辑计算,直到数组中只有一个元素即结果为止
流程如下:
实践如下:
1 |
|
计算代码如下:
1 |
|
补充:
如果只想要表达式的计算结果,不需要使用过程,则可以直接使用 NSExpression
计算表达式的结果,代码如下:
1 | // 使用 NSExpression 计算表达式的结果 |
总结
代码链接:https://github.com/mokong/ExpressionCalculator
代码效果: