本文字数:8396字
预计阅读时间:21分钟
背景
最近在开发《挑战24点》的过程中遇到了一个问题,即,如何计算常用数学表达式的结果,比如,给定字符串8 - (6 + 4 / 2 - 1) * 2
,怎么计算得到结果,并且得到计算的过程。
网上查资料发现,大部分都是类似系统计算器的处理,在遇到第二个运算符时,就把前一步的操作结果计算出来。这样的处理方式并不适用 于笔者想要解决的问题。
进一步搜索后发现,前缀表达式、中缀表达式、后缀表达式的概念,给定的字符串8 - (6 + 4 / 2 - 1) * 2
属于中缀表达式,而想要计算机得出结果,可以转为前缀表达式或者后缀表达式,然后再对转换后的表达式进行计算。
这里采用中缀表达式转后缀表达式,然后计算后缀表达式得出结果,步骤如下。
Swift 中缀表达式转后缀表达式
什么是中缀表达式、后缀表达式?
首先理解中缀表达式和后缀表达式分别是什么?
中缀表达式: 是常用的算术表示方法,操作符处于操作数的中间,比如 (a + b),即中缀形式,故而称之为中缀表达式。(https://baike.baidu.com/item/%E4%B8%AD%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F/2725244)
后缀表达式: 运算符写在操作数之后,比如 (a, b, +),称之为后缀表达式,又名逆波兰式。(https://baike.baidu.com/item/%E9%80%86%E6%B3%A2%E5%85%B0%E5%BC%8F/128437?fromtitle=%E5%90%8E%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F&fromid=6160580)
为什么要把中缀表达式转为后缀表达式?
为什么要将简单的中缀表达式转为后缀表达式呢?因为中缀表达式的简单对于计算机来说是非常复杂的,没有办法直接运算,而后缀表达式对于计算机而言是简单易懂的结构。所以计算常见的表达式时,要转为后缀表达式,然后运算。
怎么转?
然后来看下,如何把中缀表达式转为后缀表达式,这里建议先看一遍,理解后,在本子上按照原理尝试一遍,更能理解。原理:
由于 Swift 中没有栈的概念,所以采用数组的实现方式,用数组 append 模拟入栈,popLast 来模拟出栈。
声明两个数组,分别用于存储数字和运算符
从左到右遍历表达式,
运算符数组中最后一个为"("时,或者要放入的运算符为"(",则不需要比较优先级,直接把要放入的运算符放入运算符数组中
如果要放入的运算符的优先级不大于运算符数组中最后一个的优先级,则把运算符数组中的最后一个弹出放入到数字数组中,直到遇到优先级比它低的时停止或者遇到"("时停止。然后把运算符放入运算符数组中。
遇到" ",继续遍历下一个字符
遇到数字则放入数字数组中
遇到")",则把运算符数组中最后一个元素弹出,直到遇到"("时停止。
遇到运算符,则比较运算符的优先级,
遍历表达式完成后,如果运算符数组不为空,则把运算符数组中的元素倒序弹出,放入到数字数组中
最后返回数字数组,即所需要的后缀表达式的数组
流程如下:
![57bda646ea763c00c7f56635950a695e.png](https://i-blog.csdnimg.cn/blog_migrate/e72229e9d947936da7a9d2a9c8f8f36f.png)
假设现有一个表达式:8 - (6 + 4 / 2 - 1) * 2
,按照上面的步骤实践如下:
// 初始化两个数组,上面为数字数组、下面为运算符数组
[]
[]
// 下一个字符为"8",是数字,直接放入数字数组中
["8"]
[]
// 下一个字符为"-",是运算符,运算符数组为空,故而不需要比较优先级,直接放入运算符数组中
["8"]
["-"]
// 下一个字符为"(",是运算符,要放入的元素是"(",不需要比较优先级,"("直接放入运算符数组中
["8"]
["-", "("]
// 下一个字符为"6",是数字,放入数字数组中
["8", "6"]
["-", "("]
// 下一个字符为"+"