C++实现对数学基本运算表达式的解析
代码地址如下:
http://www.demodashi.com/demo/11078.html前段时间在LeetCode上刷题,遇到了很多涉及对字符串进行解析的题目。可能是出于这个原因,最近迷恋上了字符串的解析问题。数学基本运算表达式的解析就涉及这类问题。所谓数学基本运算表达式的解析就是指给定一个表达式字符串,如1 + 1,3 * 9,对这个字符串进行解析,从而得到这个表达式的运算结果。(数学基本运算表达式也就是只用加减乘除进行计算的数学表达式)
其实站在我的角度来看,我觉得对数学基本运算表达式的解析还是有一定难度的。因为如果一开始没有正确的思路,我们是很难找到这个问题的着手点的,毕竟解析数学基本运算表达式需要考虑到的问题是有点多的,以下,我把其中主要的问题列举出来:
- 乘除法优先计算
- 括号里的内容优先计算
- 表达式中的数字前可能存在正负号
这些问题如果得不到恰当的处理,就会使解析过程失败。
在实现解析数学基本运算表达式之前,我们首先得弄清楚哪些表达式是合法的,哪些表达式是不合法的。以下我将列举C/C++等语言中,一些合法与不合法的表达式:
/* 合法 */
7 + 22 + 7 * 27
6 + -13 * 23 / -30 / 35
24 - +34
10 + (3 * 9) / 8/* 不合法 */
abc + 123 // abc不是数字
1 + 2 * // *后缺少数字
6 + + 12 // 不能连用加号
8 - - 17 // 不能连用减号
32 + () - 4 // 括号中没有内容
(0 + 5)) * 7 // 右括号多了一个
((11 + 9) / 2 // 左括号多了一个
不排除有一些编程语言支持上述所说的部分或者全部不合法表达式,但这里我们先使用C/C++标准。
原理讲解
由于实现的过程很“绕圈子”,所以我就先把我的实现思路和原理告诉大家,供大家参考。
(可能网上有大佬提供了更好、更高效的解析数学表达式方法,不过我认为我的处理方式是非常直白易懂的一种。)
首先,我们来回忆一下我们做数学计算时的情形:
第一步
如果数学运算表达式存在括号的话,我们会率先找到括号里的内容,并将括号里的内容当作一个新的数学表达式进行优先运算。将这个新的数学表达式进行运算后,用得到的结果将括号及括号间的内容替换掉。当我们把所有括号里的内容都用相应的结果替换掉后,就能得到原先数学表达式消去括号后的简化式子,然后我们再对这个式子进行处理。例如原有的数学式7 * (5 + 3),进行消去括号的处理后,就得到了7 * 8,接下来我们再对这个式子进行解析和计算,就能得到答案。
第二步
消去括号的数学基本运算表达式就只剩加减乘除符号以及数字、小数点(如果有小数的话)了。由于乘法和除法要优先运算,如果式子中有这两种运算的话,我们就要率先在式子中进行乘法运算和除法运算。运算的过程中,我们采用的做法同样是把运算得到的结果替换掉原来的运算式子。如下计算过程示例可能会让你重拾这一过程:
7 + 18 * 5 / 9|v
7 + 90 / 9|v
7 + 10
第三步
进行完前两步的处理,剩下的式子就只有加减符号以及数字、小数点(如果有小数的话)了。用和第二步中类似的做法进行计算,最后,式子就化成了数字,这个数字就是我们的答案。以第二步示例中最后得到的式子为例,7 + 10运算得到17,17就是最后的答案。
现在,我们回忆完了平日里我们进行数学计算的步骤,其实这就是一个对表达式逐渐化简的过程。接下来我们就要从这些步骤中构思我们的解析算法。
在第一步中,我们提到了对括号里的内容进行优先计算。但是我们可能会遇到括号套括号的问题。但是正如第一步中所说,我们可以把括号里的式子当作新的一个式子来处理,对于新的式子又可以采用第一步到第三步的方式依次进行处理得到结果,其中的第一步又可以对新式子中的子括号进行处理。这样一来就形成了一种递归关系。所以,我们只用实现如下几个接口,就能完成对数学基本运算式的解析:
- 接口A:处理运算式的统一接口
- 接口B:处理括号的接口
- 接口C:处理运算符的接口,一次可处理两种运算符(因为四则运算符可以分成 乘除一组,加减一组)
- 接口D:基本运算接口,即针对两个数字之间四则运算的接口(因为任意的数学表达式都可以通过两两计算进行化简求值)
- 接口E:处理字符串的一个接口集合,提供了剔除字符串左右空白或者所有空白的接口、float与std::string相互转化的接口(为了方便小数计算,使用float类型)
第一步用接口B来完成,第二、三步用接口C来完成。接口C中使用接口D来进行结果运算,即接口C进行的操作是找到运算符并获取运算符两边的数字,然后交给接口D对这两个数字按照找到的运算符进行计算,再将得到的计算结果替换掉式子中相应的部分。而接口E为前几个接口提供辅助功能。
接口A是调用其他接口的一个入口。接口A~D的伪代码如下:
/*** 接口A* @param exp 需要解析的表达式*/
float getValue(const string& exp)
{// 剔除表达式字符串的左右空白string __exp = 接口E(exp);// 处理括号handleBrackets(__exp);// 处理乘法除法运算handleOperator(__exp, pair<string, string>('*', '/'));// 处理加法减法运算handleOperator(__exp, pair<string, string>('+', '-'));// 将`std::string`转化为`float`float res = 接口E(__exp);// 返回结果return res;
}/*** 接口B* @param exp 需要解析的表达式*/
void handleBrackets(string& exp)
{for 遍历`exp` {if 遇到括号 {string content = 获取括号内容;// 将括号里的内容作为新的表达式处理int val = getValue(content);// 将`float`转化为`std::string`string valStr = 接口E(val);将`exp`括号部分替换为valStr;更新`exp`长度和遍历的下标位置;}}
}/*** 接口C* @param exp 需要解析的表达式* @param operators 需要处理的运算符号(乘除一组,加减一组)*/
void handleOperator(string& exp, pair<string, string> operators)
{for 遍历`exp` {if 遇到operators中的运算符 {string strVal1 = 获取进行计算的数字1(运算符左侧数字);string strVal2 = 获取进行计算的数字2(运算符右侧数字);// 获取计算结果float v = basicCalc(strVal1, 运算符, strVal2);// 将`float`转化为`std::string`string valStr = 接口E(v);将`exp`括号部分替换为valStr;更新`exp`长度和遍历的下标位置;}}
}/*** 接口D* @param s1 用于计算的数字1* @param _operator 运算符* @param s2 用于计算的数字2*/
void basicCalc(const string& s1, const string& _operator, const string& s2)
{// 将`std::string`转化为`float`float n1 = 接口E(s1);float n2 = 接口E(s2);根据运算符_operator进行n1 n2之间的计算;
}
(至于接口E,由于是一个提供辅助功能的接口集,所以上面的伪代码中没有给出。具体实现方法请参考给出的源代码)
文件截图:
上面的伪代码还只是一个骨架,没有血肉,还不能解决一些实际的问题(比如前文提到的如何处理括号,如何解决数字前的正负号等问题)。大家可以参考我给出的完整实现代码。
当然,各位读者也可以根据我前面的原理讲解自行实现算法。C++实现对数学基本运算表达式的解析
代码地址如下:
http://www.demodashi.com/demo/11078.html注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权
C++实现对数学基本运算表达式的解析相关推荐
- matlab中非线性回归标准误,SPSS—非线性回归(模型表达式)案例解析_spss培训
SPSS-非线性回归(模型表达式)案例解析 由简单到复杂,人生有下坡就必有上坡,有低潮就必有高潮的迭起,随着SPSS的深入学习,已经逐渐开始走向复杂,今天跟大家交流一下,SPSS非线性回归,希望大家能 ...
- MySQL(五)-数学函数的使用解析
数学函数的使用解析 1 绝对值函数ABS(X)和返回圆周率函数PI() 2 平方根函数SQRT(X)和求余函数MOD(X,Y) 3 获取整数函数CEIL(X),CEILING(X),FLOOR(X) ...
- 4 三元表达式 列表解析 生成器表达式
三元表达式 #!/usr/bin/env python3 # _*_ coding:utf-8 _*_ # @File : 三元表达式 # @Version : 1.0'''三元表达式能实现简单的 i ...
- 干货!表达式树解析框架(3)
最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 这应该是年前最后一篇了,接下来的时间就要陪陪老婆孩子了 关于表达 ...
- JSP页面EL表达式不解析
转载自 JSP页面EL表达式不解析 问题是这样:在搭建springMVC环境的时候,笔者写了一个简单的Controller如下: @Controller public class HelloCont ...
- 三元表达式列表解析生成器表达式
三元表达式 列表解析 生成器表达式 三元表达式 在作简单的判断时,三元表达式能简化代码: def max(x, y):if x > y:return xelse:return y # 这个函数可 ...
- 张宇考研数学闭关修炼【解析分册】
张宇考研数学闭关修炼[解析分册] 链接: https://pan.baidu.com/s/1cYS9Tgb2cF4c9tUIhYRLMw 提取码: mud7 复制这段内容后打开百度网盘手机App,操作 ...
- spring cron表达式及解析过程
1.cron表达式 cron表达式是用来配置spring定时任务执行时间的字符串,由5个空格分隔成的6个域构成,格式如下: {秒} {分} {时} {日} {月} {周} 每一个域的含义解释 ...
- 2021汤家凤强化测试10套卷【数学二】【解析】
2021汤家凤强化测试10套卷[数学二][解析] 链接: https://pan.baidu.com/s/13B-yDYrxEXord5ybw9E5sg 提取码: irfn 复制这段内容后打开百度网盘 ...
最新文章
- 系统中多种隐藏超级用户添加方法第1/2页
- python容器数据类型_python collections 容器数据类型
- vue2实现自定义样式radio单选框
- VS2010下测试程序性能瓶颈
- 7-48 字符串输入练习 (I) (15 分)
- Oracle 声明常量
- Url传值的Get method and Post method
- 【颜色识别】基于matlab机器视觉颜色识别系统【含Matlab源码 588期】
- 2022电工(中级)考试模拟100题模拟考试平台操作
- AMOS软件简介【SPSS 050期】
- PyQt、PySide、PySide2这三者到底有什么区别?
- 数字电视业务PSI-SI学习系列
- 【文件上传】——文件上传后需要重启服务器方可访问到异常
- 微信公众号新手注册,创建菜单栏,发布文章详细操作教程
- 壹沓科技签约七匹狼,助力「中国男装领导者」数字化转型
- java的JVM虚拟机相关知识,简单易懂。
- 5.1 规定“学什么”的培养方案——《逆袭大学》连载
- Swagger注解详解
- 判断2000-2500年中的每一年是否为闰年,并将结果输出。
- SIMATIC G120 故障报A07991解决方法
热门文章
- mysql统计最高_MySql-统计所有会员的最高前10次的积分和
- httpclient依赖_.NetCore 3.1高性能微服务架构:封装调用外部服务的接口方法HttpClient客户端思路分析...
- 18.QThread线程创建
- mysql 字段操作_Mysql:数据库操作、数据表操作、字段操作整理
- 使用cadence建封装
- kinect1+depthimage_to_laserscan
- 网关gateway解决跨域问题
- linux收集完整技术支持信息的命令有,Linux下常用的日志收集命令(RedhatSuSe)
- linux虚拟机上离线安装mysql_Linux下离线安装MySQL
- url中传递url参数|url中特殊字符、?、=无法解析问题