代码地址如下:
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++实现对数学基本运算表达式的解析的更多相关文章

  1. 轻量级表达式树解析框架Faller

    有话说 之前我写了3篇关于表达式树解析的文章 干货!表达式树解析"框架"(1) 干货!表达式树解析"框架"(2) 干货!表达式树解析"框架" ...

  2. 干货!表达式树解析"框架"(1)

    最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 关于我和表达式树 其实我也没有深入了解表达式树一些内在实现的原理 ...

  3. 干货!表达式树解析"框架"(2)

    最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 为了过个好年,我还是赶快把这篇完成了吧 声明 本文内容需要有一定 ...

  4. 干货!表达式树解析"框架"(3)

    最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 这应该是年前最后一篇了,接下来的时间就要陪陪老婆孩子了 关于表达 ...

  5. 介绍一个可以将Expression表达式树解析成Transact-SQL的项目Expression2Sql

    一.Expression2Sql介绍 Expression2Sql是一个可以将Expression表达式树解析成Transact-SQL的项目.简单易用,几分钟即可上手使用,因为博主在设计Expres ...

  6. C#数学运算表达式解释器

    C#数学运算表达式解释器 測试文件内容: a=2+3*2; b=2*(2+3); 浏览按钮事件处理程序: private void button_browse_Click(object sender, ...

  7. 表达式树解析"框架"

    干货!表达式树解析"框架"(2)   为了过个好年,我还是赶快把这篇完成了吧 声明 本文内容需要有一定基础的开发人员才可轻松阅读,如果有难以理解的地方可以跟帖询问,但我也不一定能回 ...

  8. jsp中的el表达式没有解析

    今天发现jsp中的el表达式没有解析,把解决的过程记录一下 在web.xml的web-app节点的版本改成2.4以上

  9. Lambda表达式树解析(下)

    概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...

随机推荐

  1. Knockout.js(二):监控数组属性(Observables Arrays)

    如果想发现并响应一个对象的变化,就应该使用监控属性(observables),如果想发现并响应一个集合的变化,就需要使用监控属性数组(observableArray).在很多情况下,它都非常有用,比如 ...

  2. 机房重构包图(从三层+实体到三层+实体+外观+工厂+接口+SQLHelper)

    刚刚开始接触三层的时候,我只做了两个登录小窗体的例子.画了简单的包图,可以说,为后面机房重构留下了大量的工作(因为三层理解没有深度,也没有理解出自己的东西).不过,欠下的总要还的.在做机房重构的时候, ...

  3. 【POJ 3974】Palindrome

    http://poj.org/problem?id=3974 Manacher模板题.Menci的博客讲得很好 有一点:Menci的代码中的right我感觉是代表能延伸到的最右端点的右边的点,因为r( ...

  4. 【最小割】【Dinic】Gym - 101128F - Landscaping

    http://blog.csdn.net/lxy767087094/article/details/68942422 #include<cstdio> #include<cstrin ...

  5. 【分类讨论】【spfa】【BFS】Codeforces Round #416 (Div. 2) D. Vladik and Favorite Game

    那个人第一步肯定要么能向下走,要么能向右走.于是一定可以判断出上下是否对调,或者左右是否对调. 然后他往这个方向再走一走就能发现一定可以再往旁边走,此时就可以判断出另一个方向是否对调. 都判断出来以后 ...

  6. 【推导】Codeforces Round #411 (Div. 1) B. Minimum number of steps

    最后肯定是bbbb...aaaa...这样. 你每进行一系列替换操作,相当于把一个a移动到右侧. 会增加一些b的数量……然后你统计一下就行.式子很简单. 喵喵喵,我分段统计的,用了等比数列……感觉智障 ...

  7. 1.3(SQL学习笔记)计算字段及函数

    一.计算字段 1.1拼接字段 一般情况下返回的字段是指定列的属性名.如果有时我们对返回格式有特殊要求. 例如,我们需要将显示商品名,即商品价格,同时商品名后面的价格放在括号内. prod_name(p ...

  8. Java高级架构师(一)第35节:Nginx的Location区段

    没有修饰符 表示:必须以指定模式开始. 表示/abc下的所有内容都可以被访问. = 表示与指定的模式精确匹配,可以带参数. 实例中要求区分大小写,并以c结尾. 实例中指定的正则表达式不区分大小写. 注 ...

  9. [转]Spring 中的p标签

    spring的bean配置文件中p:代表什么 <bean id="daoTemplate" abstract="true" lazy-init=" ...

  10. 打造百度网盘备份利器:自动备份Linux VPS文件和多线程下载百度网盘资源

    前一段时间国内的各大网盘百度云盘,金山快盘,360云盘,华为网盘为争夺用户上演空间容量博弈,网盘商们还固执地以为中国的网民都不懂网络技术,可以像某公司那样用一些数字的手段来忽悠用户,参与到网盘商的数字 ...