• 表达式求值(栈方法/C++语言描述)(一)


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

    1 typedef enum {
    2     BEGIN,
    3     NUMBER,
    4     OPERATOR,
    5     LEFT_BRAC,
    6     RIGHT_BRAC
    7 } TokenType;

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

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

     1 class Calculator
     2 {
     3 public:
     4     double calculate(string expression) throw(string);
     5 
     6 private:
     7     stack<double> _stkNumbers;
     8     stack<char> _stkOperators;
     9 
    10     static int priority(char op);
    11     static double calculate(double d1, char op, double d2) throw(string);
    12 
    13     void calculateStack() throw(string);
    14     void dealWithNumber(char *&pToken) throw(string);
    15     void dealWithOperator(char *&pToken) throw(string);
    16     void dealWithLeftBrac(char *&pToken) throw(string);
    17     void dealWithRightBrac(char *&pToken) throw(string);
    18 };

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

    1 void Calculator::calculateStack() throw (string) {
    2     double d2 = _stkNumbers.top();
    3     _stkNumbers.pop();
    4     double d1 = _stkNumbers.top();
    5     _stkNumbers.pop();
    6     char op = _stkOperators.top();
    7     _stkOperators.pop();
    8     _stkNumbers.push(calculate(d1, op, d2));
    9 }

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

     1 double Calculator::calculate(double d1, char op, double d2) throw (string) {
     2     assert(op == '+' || op == '-' || op == '*' || op == '/');
     3 
     4     cout << d1 << op << d2 << endl;
     5 
     6     if (op == '+') {
     7         return d1 + d2;
     8     } else if (op == '-') {
     9         return d1 - d2;
    10     } else if (op == '*') {
    11         return d1 * d2;
    12     } else {
    13         if (!d2) {
    14             throw string("divided by 0");
    15         }
    16         return d1 / d2;
    17     }
    18 }

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

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

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

     1 double Calculator::calculate(string expression) throw (string) {
     2     while (!_stkNumbers.empty()) {
     3         _stkNumbers.pop();
     4     }
     5     while (!_stkOperators.empty()) {
     6         _stkOperators.pop();
     7     }
     8     TokenType lastToken = BEGIN;
     9 
    10     char * pToken = &expression[0];
    11     while (*pToken) {
    12         switch (lastToken) {
    13         case BEGIN:
    14             if (*pToken == '(') {
    15                 // an expression begin with a left bracket
    16                 dealWithLeftBrac(pToken);
    17                 lastToken = LEFT_BRAC;
    18             } else {
    19                 // or a number
    20                 dealWithNumber(pToken);
    21                 lastToken = NUMBER;
    22             }
    23             break;
    24         case NUMBER:
    25             // after a number
    26             if (*pToken == ')') {
    27                 // it may be a right bracket
    28                 dealWithRightBrac(pToken);
    29                 lastToken = RIGHT_BRAC;
    30             } else {
    31                 // it may be an operator
    32                 dealWithOperator(pToken);
    33                 lastToken = OPERATOR;
    34             }
    35             break;
    36         case OPERATOR:
    37         case LEFT_BRAC:
    38             // after an operator or a left bracket
    39             if (*pToken == '(') {
    40                 // it may be a left bracket
    41                 dealWithLeftBrac(pToken);
    42                 lastToken = LEFT_BRAC;
    43             } else {
    44                 // it may be a number
    45                 dealWithNumber(pToken);
    46                 lastToken = NUMBER;
    47             }
    48             break;
    49         case RIGHT_BRAC:
    50             // after a right bracket
    51             if (*pToken == ')') {
    52                 // it may be another right bracket
    53                 dealWithRightBrac(pToken);
    54                 lastToken = RIGHT_BRAC;
    55             } else {
    56                 // it may be an operator
    57                 dealWithOperator(pToken);
    58                 lastToken = OPERATOR;
    59             }
    60             break;
    61         }
    62     }
    63 
    64     while (!_stkOperators.empty()) {
    65         if (_stkOperators.top() == '(') {
    66             throw string("bad token '('");
    67         }
    68         calculateStack();
    69     }
    70 
    71     assert(!_stkNumbers.empty());
    72     return _stkNumbers.top();
    73 }

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

  • 相关阅读:
    Overview of .rdp file settings
    Html事件冒泡
    文件复制cp的操作及scp的运用
    防火墙操作
    批量添加文件夹
    linux查看历史输入命令history
    linux 批量删除文件
    服务器磁盘空间不足的问题
    linux查看CPU、内存、磁盘大小
    tomcat启动成功但是无法访问ip地址及端口问题解决始末
  • 原文地址:https://www.cnblogs.com/lets-blu/p/7265961.html
Copyright © 2020-2023  润新知