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 << '\t'<< res << ":" << CalExp(res.split(' ', QString::SkipEmptyParts));
22     qDebug() << reExp2 << '\t'<< res2 << ":" << CalExp(res2.split(' ', QString::SkipEmptyParts));
23     qDebug() << reExp3 << '\t'<< res3 << ":" << CalExp(res3.split(' ', QString::SkipEmptyParts));
24     qDebug() << reExp4 << '\t'<< res4 << ":" << CalExp(res4.split(' ', QString::SkipEmptyParts));
25     qDebug() << reExp5 << '\t'<< 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實現)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM