找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 2433|回复: 1

C++计算四则运算表达式程序

[复制链接]
发表于 2018-12-6 17:31:39 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
注1:这是本人2016年的旧文,特发至宝地,望抛砖引玉。

注2:源码:https://github.com/tomwillow/ExpressionCalc

注3:表达式树计算我是学习自中国大学MOOC 台湾清华韩永楷老师的《数据结构》课程。有兴趣的同学可以看视频进行学习。

注4:这个表达式树的原理我后来用在了编写连杆机构仿真中的方程计算类里面,能以字符串输入求解线性/非线性方程,并且我定义了几个类似于MATLAB的接口,可以单独把计算类抽出来用。详情见Github:
https://github.com/tomwillow/Linkage-Mechanism-Laboratory
————

最近在学数据结构,刚学完Expression Tree,解答了我多年的疑惑。以前就想写一个二十四点的小游戏,计算机发4张牌,玩家在规定时间内想出4张牌任意四则运算后得到24的表达式。框架搭好,也可以发牌了,电脑怎么答题可以遍历所有可能性,得到24就中止。但玩家输入表达式,怎么计算出值是关键问题。现在终于知道解法了。

1.PNG

  1. #include <iostream>
  2. #include <stack>
  3. #include <queue>

  4. using namespace std;

  5. int isNumber(char c);

  6. /* 单个元素 */
  7. struct Element
  8. {
  9.     int type;//0:数字  1:运算符
  10.     int value;//数字的值
  11.     char operate[5];//运算符
  12.     int op_num;//操作数个数
  13.     bool left2right;//运算顺序左结合
  14. };

  15. /* 返回运算符结合性 */
  16. bool isLeft2Right(char c)
  17. {
  18.     switch (c)
  19.     {
  20.     case '+':
  21.     case '-':
  22.     case '*':
  23.     case '/':
  24.     case '&':
  25.     case '|':
  26.     case '%':return true;
  27.     case '^':return false;
  28.     }
  29. }

  30. /* 返回运算符的优先级 */
  31. int Rank(char s[])
  32. {
  33.     switch (s[0])
  34.     {
  35.     case '(':return 0;
  36.     case ')':return 0;//左右括号优先级小是为了不被其余任何运算符挤出
  37.     case '+':
  38.     case '-':return 5;//低优先级将挤出高优先级
  39.     case '*':
  40.     case '/':return 10;
  41.     case '^':return 11;
  42.     case '&':
  43.     case '|':return 12;
  44.     case '%':return 15;//取余运算
  45.     }
  46.     int err=-1;
  47.     if (isNumber(s[0])) err=0;
  48.     return err;
  49. }

  50. /* 是运算符 */
  51. int isOperator(char c)
  52. {
  53.     switch (c)
  54.     {
  55.     case '(':
  56.     case ')':return 10;
  57.     case '+':
  58.     case '-':
  59.     case '*':
  60.     case '/':
  61.     case '^':
  62.     case '&':
  63.     case '|':
  64.     case '%':return 1;
  65.     }
  66.     return 0;
  67. }

  68. /* 有效性检查(返回0则出现异常字符) */
  69. int isLegal(char c)
  70. {
  71.     if (isNumber(c)) return 1;
  72.     if (isOperator(c)) return 1;
  73.     return 0;
  74. }

  75. int isNumber(char c)
  76. {
  77.     if (c>='0' && c<='9')
  78.         return 1;
  79.     else
  80.         return 0;
  81. }

  82. /* 由位数得到应该相乘的倍数 如digit=2返回100 */
  83. int digit2num(int digit)
  84. {
  85.     int temp=1;
  86.     digit--;
  87.     while (digit)
  88.     {
  89.         temp*=10;
  90.         digit--;
  91.     }
  92.     return temp;
  93. }

  94. /*由in order队列得到post order队列*/
  95. int InQueue2PostQueue(queue<Element> *post,queue<Element> *in)//返回0:正常 1:括号不配对
  96. {
  97.     int sq_err=0;
  98.     stack<Element> temp;
  99.     while (in->size()>0)
  100.     {
  101.         if (in->front().type==0)
  102.         {
  103.             post->push(in->front());
  104.             in->pop();
  105.         }
  106.         else
  107.         {
  108.             if (in->front().operate[0]=='(') //左括号直接入栈
  109.             {
  110.                 temp.push(in->front());
  111.                 in->pop();
  112.                 sq_err++;
  113.             }
  114.             else
  115.             {
  116.                 if (in->front().operate[0]==')')//出现右括号
  117.                 {
  118.                     while (temp.size()>0)
  119.                     {
  120.                         if (temp.top().operate[0]=='(')
  121.                         {
  122.                             temp.pop();
  123.                             sq_err--;
  124.                             break;
  125.                         }
  126.                         else
  127.                         {
  128.                             post->push(temp.top());
  129.                             temp.pop();
  130.                         }
  131.                     }
  132.                     in->pop();
  133.                 }
  134.                 else//不是括号
  135.                 {
  136.                     if (temp.size()>0 && temp.top().left2right==true)//左结合
  137.                         while (temp.size()>0 && Rank(in->front().operate)<=Rank(temp.top().operate))//临时栈有内容,且新进符号优先级低,则挤出高优先级及同优先级符号
  138.                         {
  139.                             post->push(temp.top());//符号进入post队列
  140.                             temp.pop();
  141.                         }
  142.                     else//右结合
  143.                         while (temp.size()>0 && Rank(in->front().operate)<Rank(temp.top().operate))//临时栈有内容,且新进符号优先级低,则挤出高优先级,但不挤出同优先级符号(因为右结合)
  144.                         {
  145.                             post->push(temp.top());//符号进入post队列
  146.                             temp.pop();
  147.                         };
  148.                     temp.push(in->front());//高优先级已全部挤出,当前符号入栈
  149.                     in->pop();
  150.                 }

  151.             }
  152.         }
  153.     }
  154.     while (temp.size()>0)
  155.     {
  156.         post->push(temp.top());
  157.         temp.pop();
  158.     }
  159.     return sq_err;
  160. }

  161. /*由字符串表达式得到in order队列*/
  162. int Str2Queue(queue<Element> *inorder,char expression[])
  163. {
  164.     int err=0;
  165.     Element tempe;
  166.     int a,b;
  167.     int type,num,sign=1;
  168.     for (int i=0;i<(int)strlen(expression);)
  169.     {
  170.         num=0;
  171.         type=isNumber(expression[i]);
  172.         if (type) a=i;
  173.         while (isNumber(expression[i])&&i<(int)strlen(expression))
  174.         {
  175.             i++;
  176.         }
  177.         if (type)//数字
  178.         {
  179.             b=i-a;//位数
  180.             while (b)
  181.             {
  182.                 num+=(expression[a]-'0')*digit2num(b);
  183.                 a++;
  184.                 b--;
  185.             }
  186.             tempe.type=0;
  187.             tempe.value=num*sign;
  188.             sign=1;//sign符号正常化
  189.             tempe.operate[0]='\0';
  190.             (*inorder).push(tempe);
  191.         }
  192.         else//不是数字
  193.         {
  194.             if ( (expression[i]=='-' && i==0)||(expression[i]=='-' && isOperator(expression[i-1])) )//
  195.             {
  196.                 sign*=-1;
  197.             }
  198.             else
  199.             {
  200.                 /* 非法性判定 */
  201.                 if (isLegal(expression[i])!=1) {err=-1;break;}//出现非法字符
  202.                 if (i==0 && isOperator(expression[i])) {err=-1;break;}//首字符出现非-的符号
  203.                 if (i+1<(int)strlen(expression) && isOperator(expression[i])==1 && expression[i+1]==')') {err=-1;break;}//出现*)形式
  204.                 if (i-1>=0 && isOperator(expression[i])==1 && (expression[i-1]=='(' || isOperator(expression[i-1])==1)) {err=-1;break;}//出现(*形式或**形式

  205.                 tempe.type=1;
  206.                 tempe.value=0;
  207.                 tempe.operate[0]=expression[i];
  208.                 tempe.operate[1]='\0';
  209.                 tempe.left2right=isLeft2Right(expression[i]);
  210.                 tempe.op_num=2;
  211.                 (*inorder).push(tempe);
  212.             }
  213.             i++;
  214.         }
  215.     }
  216.     return err;
  217. }

  218. /*由运算符c及a,b计算出值*/
  219. int CalcNum(int a,int b,char c)
  220. {
  221.     switch (c)
  222.     {
  223.     case '+':return a+b;
  224.     case '-':return a-b;
  225.     case '*':return a*b;
  226.     case '/':return a/b;
  227.     case '%':return a%b;
  228.     case '&':return a&b;
  229.     case '|':return a|b;
  230.     case '^':return (int)pow((double)a,(double)b);
  231.     }
  232. }

  233. /*由post order序列计算出值*/
  234. int Calc(queue<Element> *post)
  235. {
  236.     Element tempe;
  237.     stack<Element> temp;
  238.     int a,b;
  239.     while (post->size()>0)
  240.     {
  241.         if (post->front().type==1)//运算符
  242.         {
  243.             b=temp.top().value;temp.pop();
  244.             a=temp.top().value;temp.pop();
  245.             tempe.type=0;tempe.operate[0]='\0';tempe.value=CalcNum(a,b,post->front().operate[0]);
  246.             temp.push(tempe);
  247.             post->pop();
  248.         }
  249.         else
  250.         {
  251.             temp.push(post->front());
  252.             post->pop();
  253.         }
  254.     }
  255.     return temp.top().value;
  256. }

  257. int main()
  258. {
  259.     cout<<"----- 表达式运算Demo -----"<<endl;
  260.     cout<<"作者:Tom Willow 2016.03.17"<<endl;
  261.     cout<<"功能:输入整数表达式计算出值。"<<endl;
  262.     cout<<"支持:加+ 减- 乘* 除/ 幂运算^ 求余% 按位与& 按位或|"<<endl;
  263.     cout<<"举例:输入6-5*(4-3+(2+1)) 返回-14"<<endl;
  264.     char expression[100];
  265.     while (1)
  266.     {
  267.         cout<<">>";
  268.         cin>>expression;

  269.         /* 1.字符串转换为in order队列 */
  270.         queue<Element> inorder,postorder;
  271.         if (Str2Queue(&inorder,expression)!=0)
  272.         {
  273.             cout<<"表达式非法。"<<endl;
  274.         }
  275.         else
  276.             /* 2.in order队列转换为post order队列 */
  277.             if (InQueue2PostQueue(&postorder,&inorder))
  278.             {
  279.                 cout<<"括号不匹配。"<<endl;
  280.             }
  281.             else
  282.                 /* 3.由post order队列计算出值 */
  283.                 cout<<Calc(&postorder)<<endl;

  284.     }
  285. }
复制代码
回复

使用道具 举报

发表于 2018-12-12 00:58:42 | 显示全部楼层
代码不错,通过学习你的代码让我又解锁了一个技能。
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-23 16:17 , Processed in 0.033017 second(s), 28 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表