Qt之加减乘除四则运算-支持负数

一、效果展示

如图1所示,是简单的四则运算测试效果,第一列为原始表达式,第二列为转换后的后缀表达式,冒号后为结果。表达式支持负数和空格,图中是使用了5组测试数据,测试结果可能不全,如大家发现算法有问题,可留言,谢谢。

图1 四则运算展示

测试代码如下

 1 void lineedit::CalculateExpression()
 2 {
 3     QString reExp("1 + 2.3 * (23 + 3)");
 4     QString res = change(reExp);//0 1 - 2.3 23 3 + * +
 5 
 6     QString reExp2("1*(-3)+2*(3+3)");
 7     QString res2 = change(reExp2);
 8 
 9     QString reExp3("2*-3+-2.1*(3+3)");
10     repairExpress(reExp3);
11     QString res3 = change(reExp3);
12 
13     QString reExp4("2*(-3)+-2.1*(3+3)");
14     repairExpress(reExp4);
15     QString res4 = change(reExp4);
16 
17     QString reExp5("2*(0-(1.1-3)*3)+-2.1*(3+3)");
18     repairExpress(reExp5);
19     QString res5 = change(reExp5);
20 
21     qDebug() << reExp << '	'<< res << ":" << CalExp(res.split(' ', QString::SkipEmptyParts));
22     qDebug() << reExp2 << '	'<< res2 << ":" << CalExp(res2.split(' ', QString::SkipEmptyParts));
23     qDebug() << reExp3 << '	'<< res3 << ":" << CalExp(res3.split(' ', QString::SkipEmptyParts));
24     qDebug() << reExp4 << '	'<< res4 << ":" << CalExp(res4.split(' ', QString::SkipEmptyParts));
25     qDebug() << reExp5 << '	'<< res5 << ":" << CalExp(res5.split(' ', QString::SkipEmptyParts));
26 }

二、一些小技巧

  在网上找了很多四则运算帖子,讲的都挺不错,思路很清晰,可是很少有拿来直接能用的,并且大多数的都不支持负数运算,既然是四则运算当然需要支持负数运算了,在这里我们只需要使用一点儿小技巧即可。

1、针对负号进行字符串修复 例如:-1*-3+2*(3+3) -> (0-1)*(0-3)+2*(3+3)。

 1 //针对负号进行字符串修复 例如:-1*-3+2*(3+3) -> (0-1)*(0-3)+2*(3+3)
 2 void repairExpress(QString & express)
 3 {
 4     bool repair = false;
 5     int lpos = -1, rpos = -1;
 6     QString result;
 7     for(int i = 0; i < express.size(); ++i)
 8     {
 9         QChar c = express[i];
10         if (c == '+' || c == '-' || c == '*' || c == '/')//出现符号时记录
11         {
12             if (repair)
13             {
14                 result.append(')');
15                 lpos = -1;
16                 repair = false;
17             }
18 
19             if (c == '-'&&
20                 (i == 0  || lpos != -1 && lpos == i - 1))
21             {
22                 result.append('(');
23                 repair = true;
24             }
25 
26             lpos = i;
27         }
28 
29         result.append(c);
30     }
31 
32     express = result;
33 }

2、为了方便后续我们计算表达式,在中缀表达式转后缀表达式时,我们在数字和负号之间加了一个空格。

1 //数字和负号之间插入空格, 方便后续计算时分割
2 void rettifyExpress(QString & express)
3 {
4     if (express.endsWith(' ') == false)
5     {
6         express.append(' ');
7     }
8 }

三、后缀表达式

中缀表达式:是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。

后缀表达式:后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。

中缀表达式转后缀表达式的方法:

1.遇到操作数:直接输出(添加到后缀表达式中)
2.栈为空时,遇到运算符,直接入栈
3.遇到左括号:将其入栈
4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。
5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈
6.最终将栈中的元素依次出栈,输出。

下边我直接给出实现代码

 1 //中缀表达式转后缀表达式
 2 QString change(const QString & s_mid)
 3 {  
 4     QString result;
 5     QStack<QChar> stk;
 6 
 7     QMap<QChar, int> op;//利用map来实现运算符对应其优先级
 8     op['(']=0;
 9     op[')']=0;
10     op['+']=1;
11     op['-']=1;
12     op['*']=2;
13     op['/']=2;
14     auto iter = s_mid.begin();
15     for(int i = 0; i < s_mid.size(); ++i)
16     {
17         QChar c = s_mid[i];
18         if (c == ' ')
19         {
20             continue;
21         }
22         if (c == '-' &&
23             (i == 0 || op.contains(s_mid[i-1])))//可能为负号
24         {
25             result.append('0');
26         }
27         if(op.contains(c))//判断该元素是否为运算符
28         {
29             if(c == ')')//情况2
30             {
31                 while(stk.top() != '(')
32                 {
33                     rettifyExpress(result);
34                     result.append(stk.top());
35                     stk.pop();
36                 }
37                 stk.pop();
38             }
39             else if(stk.empty() || c == '(' || op[c] > op[stk.top()])//情况1、情况3
40             {
41                 stk.push(c);
42             }
43             else if(op[c] <= op[stk.top()])//情况3
44             {
45                 while(op[c] <= op[stk.top()] && (!stk.empty()))
46                 {
47                     rettifyExpress(result);
48                     result.append(stk.top());
49                     stk.pop();
50                     if(stk.empty()) break;
51                 }
52                 stk.push(c);
53             }
54 
55             rettifyExpress(result);
56         }
57         else
58         {
59             result.append(c);
60         }
61     }
62 
63     while(stk.empty() == false)//当中缀表达式输出完成,所有元素出栈
64     {
65         rettifyExpress(result);
66         result.append(stk.top());
67         stk.pop();
68     }
69 
70     return result;
71 }
View Code

四、表达式计算

通过后缀表达式计算时,我们就不需要考虑优先级了,只需要严格按照从左向右,遇到负号取之前的两个数值进行计算即可。

 1 //计算表达式值
 2 double CalExp(const QStringList & express)
 3 {
 4     double result;
 5     QStack<QString> stk;
 6     for (int i = 0; i < express.size(); ++i)
 7     {
 8         QString item = express[i];
 9         if (item.size() == 1 && 
10             (item.at(0) == "+" || item.at(0) == "-" || item.at(0) == "*" || item.at(0) == "/"))
11         {
12             double r = stk.pop().toDouble();
13             double l = stk.pop().toDouble();
14             switch(item.at(0).toLatin1())
15             {
16             case '+':
17                 result = l + r;break;
18             case '-':
19                 result = l - r;break;
20             case '*':
21                 result = l * r;break;
22             case '/':
23                 result = l / r;break;
24             }
25 
26             stk.push_back(QString::number(result));
27         }
28         else 
29         {
30             stk.push_back(item);
31         }
32     }
33 
34     return result;
35 }

五、下载链接

  Qt之加减乘除四则运算-支持负数

参考文章:

1、四则运算表达式树 C++模板 支持括号和未知数

2、中缀表达式得到后缀表达式(c++、python实现)

原文地址:https://www.cnblogs.com/swarmbees/p/9356346.html