一个算数表达式(以下简称为表达式)由运算数、运算符、左括号和右括号组成,定义一个枚举类型TokenType表示为:

 typedef enum {
BEGIN,
NUMBER,
OPERATOR,
LEFT_BRAC,
RIGHT_BRAC
} TokenType;

BEGIN用来表示表达式的开始,稍后会再提及到它。

  对表达式进行求值需要借助数据结构栈,C++的标准模板库中包含stack类型,只需要包含头文件stack并引用命名空间std就可以使用了。整个求值过程总共需要2个栈,分别用来存储运算数和运算符;基本求值过程是这样的:比较当前运算符和运算符栈中栈顶运算符的优先级,若栈顶运算符优先级高于当前运算符,则从运算数栈中弹出两个运算数,使用运算符栈栈顶运算符进行计算后再压入运算数栈,并将当前运算符压入运算符栈,否则只将当前运算符压入运算符栈;最后反复上述运算压栈过程直至运算符栈为空,运算数栈的栈顶元素即为运算结果。Calculator类声明如下:

 class Calculator
{
public:
double calculate(string expression) throw(string); private:
stack<double> _stkNumbers;
stack<char> _stkOperators; static int priority(char op);
static double calculate(double d1, char op, double d2) throw(string); void calculateStack() throw(string);
void dealWithNumber(char *&pToken) throw(string);
void dealWithOperator(char *&pToken) throw(string);
void dealWithLeftBrac(char *&pToken) throw(string);
void dealWithRightBrac(char *&pToken) throw(string);
};

使用代码描述的运算压栈过程如下:

 void Calculator::calculateStack() throw (string) {
double d2 = _stkNumbers.top();
_stkNumbers.pop();
double d1 = _stkNumbers.top();
_stkNumbers.pop();
char op = _stkOperators.top();
_stkOperators.pop();
_stkNumbers.push(calculate(d1, op, d2));
}

静态成员函数calculate()用于进行简单的四则运算,同时也处理了除数为0的情况:

 double Calculator::calculate(double d1, char op, double d2) throw (string) {
assert(op == '+' || op == '-' || op == '*' || op == '/'); cout << d1 << op << d2 << endl; if (op == '+') {
return d1 + d2;
} else if (op == '-') {
return d1 - d2;
} else if (op == '*') {
return d1 * d2;
} else {
if (!d2) {
throw string("divided by 0");
}
return d1 / d2;
}
}

  此外,根据数学规则,有:

  • 表达式只能以左括号或运算数开始;
  • 运算数后只能是右括号或运算符;
  • 运算符或左括号后只能是左括号或运算数;
  • 右括号后只能是另一个右括号或运算符。

使用代码描述这些数学规则和最后清空运算符栈的过程如下:

 double Calculator::calculate(string expression) throw (string) {
while (!_stkNumbers.empty()) {
_stkNumbers.pop();
}
while (!_stkOperators.empty()) {
_stkOperators.pop();
}
TokenType lastToken = BEGIN; char * pToken = &expression[];
while (*pToken) {
switch (lastToken) {
case BEGIN:
if (*pToken == '(') {
// an expression begin with a left bracket
dealWithLeftBrac(pToken);
lastToken = LEFT_BRAC;
} else {
// or a number
dealWithNumber(pToken);
lastToken = NUMBER;
}
break;
case NUMBER:
// after a number
if (*pToken == ')') {
// it may be a right bracket
dealWithRightBrac(pToken);
lastToken = RIGHT_BRAC;
} else {
// it may be an operator
dealWithOperator(pToken);
lastToken = OPERATOR;
}
break;
case OPERATOR:
case LEFT_BRAC:
// after an operator or a left bracket
if (*pToken == '(') {
// it may be a left bracket
dealWithLeftBrac(pToken);
lastToken = LEFT_BRAC;
} else {
// it may be a number
dealWithNumber(pToken);
lastToken = NUMBER;
}
break;
case RIGHT_BRAC:
// after a right bracket
if (*pToken == ')') {
// it may be another right bracket
dealWithRightBrac(pToken);
lastToken = RIGHT_BRAC;
} else {
// it may be an operator
dealWithOperator(pToken);
lastToken = OPERATOR;
}
break;
}
} while (!_stkOperators.empty()) {
if (_stkOperators.top() == '(') {
throw string("bad token '('");
}
calculateStack();
} assert(!_stkNumbers.empty());
return _stkNumbers.top();
}

lastToken用来指示上一个token的类型,它应该被初始化为BEGIN;在开始求值之前清空运算符栈和运算数栈,可以防止出错,是很有必要的。

表达式求值(栈方法/C++语言描述)(一)的更多相关文章

  1. 表达式求值(栈方法/C++语言描述)(二)

    上篇中完成了对表达式求值的整体过程,接下来看看如何处理不同类型的token. 对运算数的处理比较简单,它直接调用函数strtod(),将字符串中的运算数转换为浮点类型并将它压入运算数栈中: void ...

  2. 第四届河南省ACM 表达式求值 栈

    表达式求值 时间限制: 1 Sec  内存限制: 128 MB 提交: 14  解决: 7 [提交][状态][讨论版] 题目描述 Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简 ...

  3. 表达式求值 (栈) 用C++实现

    #include <cstdio> #include <cstdlib> #include <cmath> #include <stack> #incl ...

  4. Python解析 算数表达式求值 栈的使用

    使用Python实现一种算数表达式求值的算法,模拟这种使用栈的方式,这是由E.W.Dijkstra在20世纪60年代发明的一种非常简单的算法.代码模拟仅仅表现一种编程思想,代码的逻辑并不完全: if ...

  5. 表达式求值(栈方法/C++语言描述)(三)

    代码清单 // calculator.h #ifndef CALCULATOR_H #define CALCULATOR_H #include <stack> #include <s ...

  6. 2015 UESTC 数据结构专题N题 秋实大哥搞算数 表达式求值/栈

    秋实大哥搞算数 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/1074 Des ...

  7. 表达式求值--数据结构C语言算法实现

    这篇博客介绍的表达式求值是用C语言实现的,只使用了c++里面的引用. 数据结构课本上的一个例题,但是看起来很简单,实现却遇到了很多问题. 这个题需要构建两个栈,一个用来存储运算符OPTR, 一个用来存 ...

  8. LeetCode:逆波兰表达式求值【150】

    LeetCode:逆波兰表达式求值[150] 题目描述 根据逆波兰表示法,求表达式的值. 有效的运算符包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说明: 整数除 ...

  9. 利用栈实现算术表达式求值(Java语言描述)

    利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...

随机推荐

  1. 框架篇:Spring+SpringMVC+Mybatis整合开发

    前言: 前面我已搭建过ssh框架(http://www.cnblogs.com/xrog/p/6359706.html),然而mybatis表示不服啊. Mybatis:"我抗议!" ...

  2. ecshop图片上传JPEG格式失败问题

    在根目录下找到includes文件目录,在其目录中找到cls_image.php打开并找到: $allow_file_types = '|GIF|JPG|JEPG|PNG|BMP|SWF|'; 此处J ...

  3. 一篇文章让你搞懂 SSL 证书

    关于结婚这件事 那天和同事讨论到底什么才算是真正的「结婚」?这种话题本来是极其不应该存在的.传统意义的领个证书,办个婚礼.吃吃喝喝,但随着社会各族人民身心发展进化,原本那些繁琐流程简直是反人类,貌似现 ...

  4. JS函数和对象(一)

    在本文章中,将对JS中的函数和对象进行一些讲解,不对之处还请之处 一.JS中的函数 1.1无参函数 其形式如下代码所示 function box(){ alert("我是一个函数,只有被调用 ...

  5. IE的hack问题浅谈

    *我一直以为是IE7的专属选择. 开始,我以为是bug ,怎么可能会有+号.一定是哪位同事写错了.找了度娘才知道自己的不足.原来也是可以的. 后来,通过ie8和ie7来调整自己的css.也感觉不用ha ...

  6. 由form表单来说说前后台数据之间的交互

    为什么从表单提交说起呢?因为大部分与后台的交互都是在form表单中实现,恰巧我入职一个月来都是在处理与后台交互的数据整合中度过,期间也发现一些小坑,出于喜欢总结,所以才想写这篇小博客. 各位童鞋,可以 ...

  7. Ionic Demo 解析

    Ionic Demo 解析 index.html 解析 1.引入所需要的类库 <link rel="manifest" href="manifest.json&qu ...

  8. [图形学] Chp18 OpenGL表面纹理函数

    以2D表面为例展示纹理贴图,用opengl设置一个2D纹理,颜色存储在32*32*3的数组中,对应的纹理坐标为0<=s, t<=1.0. 画出几个正方形表面,分别以GL_CLAMP(纹理坐 ...

  9. Chrome浏览器扩展开发系列之十一:NPAPI插件的使用

    在Chrome浏览器扩展中使用HTML和JavaScript非常容易,但是如何重用已有的非JavaScript遗留系统代码呢?答案是将NPAPI插件绑定到Chrome浏览器扩展,从而实现在Chrome ...

  10. discuz 6.1.0F前台getshell(据说通用6.x , 7.x)

    EXP: 执行phpinfo()语句: GLOBALS[_DCACHE][smilies][searcharray]=/.*/eui; GLOBALS[_DCACHE][smilies][replac ...