2020-08-14 基于栈进行简单四则运算(带括号)

前言:

本例程参考 《数据结构 C语言》版本内 表达式求值一例对于堆栈进行深入研究,后续由于进行重复计算,改用 vector 作为容器但在思维上依旧采用栈先入后出。
核心就是通过当前符号与前一符号进行权重比较,选择计算,消去括号或则只写入符号不运算的方式。

改进点:

1 原先将 前符号位 ) 都需进行运算,这样 )+ 这类实际是有问题改为 < 只填写符号(具体看程序)
2 引入 _third 值用于处理 )+2# 这种情况,这是不需要计算的
3 一次计算完成之后通过字符拼接重新构成一个字符串,思考逻辑通拆解计算的方式类似也是前后符号比较权重 combinationSymbol
4 可以简单处理 () 关系,但暂时无法处理 ()) 数量不匹配等问题,可在计算完成之后,进行符号匹配检查
5 输入的运算符号可以通过检查进行识别,暂未处理
6 是应用上可按需调整

// CMakeStudio.cpp: 定义应用程序的入口点。
//

#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include <tuple>
#include <optional>
#include <functional>

using namespace std;

/**
 * \brief 判断前后运算符大小,对于 > 则进行计算 < 不处理 = 消去括号 J 表示结束 E 表示错误
 * \remark
 *     +      -      *      /      (      )      #
 * +   >      >      <      <      <      >      >
 * -   >      >      <      <      <      >      >
 * *   >      >      >      >      <      >      >
 * /   >      >      >      >      <      >      >
 * (   <      <      <      <      <      =      E
 * )   <      <      <      <      E      <      <
 * #   <      <      <      <      <      E      J
 *
 * \param _first
 * \param _second
 * \param _third 这个参数默认是 \0 是为了应对 )+2# 这种情况时,只需要按照 < 方式处理
 * \return
 */
char getPrecede(const char& _first, const char& _second, char _third = '\0')
{
       char res{ '%' };

       //对于 )+2# 这种情况做单独处理,不需要进行计算
       if ((_first == '+' || _first == '-' || _first == '*' || _first == '/') && 
_second == '#' && ')' == _third) {
              return '<';
       }

       if (_first == '+' || _first == '-') {
              switch (_second)
              {
              case '+':
              case '-':
              case ')':
              case '#':
                     res = '>'; break;
              case '*':
              case '/':
              case '(':
                     res = '<'; break;
              }

       }
       else if (_first == '*' || _first == '/') {
              switch (_second)
              {
              case '(':
                     res = '<'; break;
              default:
                     res = '>'; break;
              }
       }
       else if (_first == '(') {
              switch (_second)
              {
              case ')':
                     res = '='; break;
              case '#':
                     res = 'E'; break;
              default:
                     res = '<'; break;
              }
       }
       else if (_first == ')') {
              if (_second == '(') {
                     return 'E';
              }
              
              return '<';
       }
       else if (_first == '#') {
              switch (_second)
              {
              case '#':
                     res = 'J'; break;
              case ')':
                     res = 'E'; break;
              default:
                     res = '<'; break;
              }
       }

       return res;
}

/**
 * \brief 专门用于供拼装字符串前后字符串关系判断
 * \remark 未标明表明为 >; E 表示异常 J 表示结束 但实际中可能并未使用该符号
 *     +      -      *      /     (      )      #
 * +                              <
 * -                              <
 * *                              <
 * /                              <
 * (                              <             E
 * )   <      <      <      <     E      <      J
 * #                              <      E      J
 * 
 * \param a1 前一个符号
 * \param a2 当前符号
 * \return char
 */
char combinationSymbol(char a1, char a2)
{
       if (a2 == '#') {
              return '='; //结束
       }

       if (a2 == '(') {
              return '<'; //只写符号
       }

       char res{ '\0' };
       if (a1 == ')') {

              switch (a2) {
              case '(':
                     res = 'E'; break; //异常
              default:
                     res = '<'; break;
              }
       }
       else {
              res = '>'; //数值+符号
       }

       return res;
}

/**
 * \brief 获取栈顶数据
 * \param _stack
 * \return
 */
char getTopValue(vector<char>& _stack)
{
       return _stack.back();
}

/**
 * \brief 消去括号
 * \param _stack
 * \return
 */
void handleBracket(vector<char>& _stack)
{
       _stack.pop_back();
}

//公式计算 (这部分可以替换成想要的内容)
optional<double> countVal(const double& _firstVal, const double& _secondVal, const 
char& _symbol)
{
       double res{ 0 };
       switch (_symbol)
       {
       case '*':
              res = _firstVal * _secondVal; break;
       case '/':
              if (0 == _secondVal) { //分母不能为 0 
                     return nullopt;
              }
              res = _firstVal / _secondVal; break;
       case '+':
              res = _firstVal + _secondVal; break;
       case '-':
              res = _firstVal - _secondVal; break;
       }
       return res;
}

/**
 * \brief 处理 前一个符号权限大于后一个符号, 并将计算结果重新放回栈中
 * \param _stack
 * \param _numStack
 * \return true 计算完成 false 计算失败
 */
bool handleMoreThan(vector<char>& _stack, vector<double>& _numStack)
{
       //至少确保有两个数字一个符号才可以进行运算
       //This function requires at least two number and one symbol for calculation
       if (_numStack.size() < 2 || _stack.size() < 1) {
              return false;
       }
       //取出值
       double _secondVal = _numStack.back();
       _numStack.pop_back();
       double _firstVal = _numStack.back();
       _numStack.pop_back();
       char _symbol = _stack.back();
       _stack.pop_back();

       //计算结果并压入栈中 计算公式存在问题
       auto res = countVal(_firstVal, _secondVal, _symbol);
       if (!res) {
              return false;
       }

       _numStack.push_back(res.value());
       return true;
}



/**
 * \brief 首尾加入 # 表示公式长度的范围 
 * \param _str [in] 传入参数
 * \param val  [out] 结果值
 * \return false 表示公式异常 true 表示公式正常; 返回 nullopt 代表计算完成
 */
optional<pair<string, bool>> getFormula(const string& _str, double& val)
{
       //所有公式都是以#作为结束符号
       string str = _str + "#";

       vector<double> opnd;
       vector<char> optr;
       optr.push_back('#');

       string tempNum{ "" }; //临时解析的数据内容
       char tempPtr{ '\0' }; 
       char tempThird{ '\0' };

       for (int i = 0, size = static_cast<int>(str.length()); i < size; i++) {
              if (str[i] >= '0' && str[i] <= '9' || str[i] == '.') {
                     tempNum += str[i];
              }
              else {
                     if (tempNum != "") {
                           opnd.push_back(stod(tempNum));
                     }

                     tempPtr = str[i];
                     //用于处理 )+3# 这种情况
                     if ('#' == tempPtr && optr.size() > 1) {
                           tempThird = optr.at(optr.size() - 2);
                     }
                     else {
                           tempThird = '\0';
                     }

                     switch (getPrecede(getTopValue(optr), tempPtr, tempThird))
                     {
                     case '<':
                           optr.push_back(tempPtr); break;
                     case '=':
                           //消去括号
                           handleBracket(optr);
                           break;
                     case '>':
                           //计算
                           if (!handleMoreThan(optr, opnd)) {
                                  return make_pair("ERR", false);
                           }
                           optr.push_back(tempPtr);
                           break;
                     case 'J':
                           //结束单纯将 # 加入到最后
                           optr.push_back(tempPtr);
                           break;
                     case 'E':
                           return make_pair("ERR", false);

                     }
                     tempNum = "";
              }
       }

       // 当符号清空或则 optr 内只有 ## 时认为计算完成,返回 over 和值
       if (optr.size() == 0) {
              val = stod(_str);
              return nullopt;
       }
       else if (optr.size() <= 2) {
              val = opnd.back();
              return nullopt;
       }
       //用于获取值的la
       function<string(vector<double>&)> getOPNDVal = [&](vector<double>& _stack) 
{
              double val = _stack.front();
              _stack.erase(_stack.begin());
              return to_string(val);
       };

       //重新构成一个字符串
       //主要是通过让当前符号与前一个符号进行比较进行堆加
       string tempStr{ "" };
       char c1 = optr.front();
       optr.erase(optr.begin());
       char c2;
       while (optr.size())
       {
              c2 = optr.front();
              optr.erase(optr.begin());

              char testAA = combinationSymbol(c1, c2);
              switch (testAA) {
              case '<': //只添加当前符号
                     tempStr += c2; break;
              case '>': //获取一个值并添加一个符号
                     if (opnd.size() > 0)
                           tempStr += getOPNDVal(opnd);
                     tempStr += c2; 
                     break;
              case '=': //添加一个值
                     if (opnd.size() > 0)
                           tempStr += getOPNDVal(opnd);
                     break;
              }
              //比对过程中都是由当前值与前一个值进行比较,而当前值已经被移除,所以采用赋值方式传递
              c1 = c2;
       }
       val = 0;
       return make_pair(tempStr, true);
}



int main()
{
       string str = "1";
       while (cin >> str) {
              double res{ 0 };
              auto val = getFormula(str, res);
              while (val) {
                     if (!val.value().second) {
                           break;
                     }
                     val = getFormula((*val).first, res);
              }
              if (val) {
                     if (!(*val).second) {
                           cout << "ERR" << endl;
                     }
              }
              else {
                     std::cout << res << endl;
              }
       }
       return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值