【Weiss】【第03章】练习3.20:中缀表达式转后缀表达式
【練習3.20】
a.編寫一個程序將中綴表達式轉換為后綴表達式,該中綴表達式含括號及四則運算。
b.把冪操作符添加到你的指令系統中去。
c.編寫一個程序將后綴表達式轉化為中綴表達式。
?
Answer:
花了好大力氣把a,b就放一起寫好了,終于知道為啥說編譯原理難了,就這么簡單的句法分析也好坑爹。
c真的不打算寫了,如果以后要學編譯原理的話再繼續吧。
?
(一)、首先是核心思路,還是比較清晰的。
①、對于左結合運算符的出入棧,規則很簡單:
如果棧頂運算符為左結合的,那么有同級別或低級別優先級運算符即將入棧時,則運算符持續彈出,直至遇左括號或棧清空為止。
解釋一下為何優先級相等時也要立即彈出,其實,對于具有結合律的操作,遇到更低級別的運算符才跳出也完全沒問題。
比如2*3*4后綴表達式可以寫成2 3 4 * * ,也可以寫成2 3 * 4 * ,無非就是2*(3*4)還是2*3*4的運算區別罷了。
但是如果對于減法這種2-3-4,寫成2 3 - 4 -才是正確的,2 3 4 - -實際上表達的是2-(3-4)=2-3+4完全和原式子不一樣。
故而,棧頂左結合運算符即使在遇到同等優先級別的運算符時,也必須跳出。
②、至于右結合運算符的出入棧,唯一也是最大的區別在于:
僅對于低級別優先級即將入棧時,才持續彈出運算符;而同級別運算符即將入棧時維持不變。
用例子2^3^4來解釋的話,數學上其定義實際上是2^3^4 = 2^(3^4)。
從而如果2^3^4采用同級別即跳出原則,會寫成2 3 ^ 4 ^即(2^3)^4,和定義不一致。
③、對于組合運算符,正括號無條件入棧,反括號無條件出棧至找到正括號為止
?
(二)、該死的錯誤檢測
因為計算后綴表達式沒寫錯誤檢測,于是這個就寫寫吧。
寫起來才知道坑,簡直是讓人不想寫c小題的最重要原因【畢竟只是在學數據結構又不是在學編譯原理啊喂(╯‵□′)╯︵┻━┻】。
想了一下,原表達式可能出現的錯誤有:
①、操作符非法
②、頭元素既不是數字,也不是正括號
③、尾元素既不是數字,也不是反括號
④、連續出現數字
⑤、連續出現操作符,當兩個操作符中沒有一個是正括號或者反括號時
⑥、反括號導致出棧時找不到對應的正括號
⑦、非反括號導致出棧時意外地發現正括號
……………………
總之這些都考慮了,也都寫了相應的錯誤,但是搞著搞著還是發現漏了情況,
比如表達式“2+(3+4)5”一看就知道是不合規的,但是它偏偏就不在前面七種情況里,你特么在逗我(╯‵□′)╯︵┻━┻!
所以,算了懶得寫了……核心功能能實現就行。
?
(三)、測試代碼
最后測了這么一段(1.1*1.2+1.3*1.4)+(1.5*1.6^1.7^(1.8+1.9))表達式,
出于懶,從原表達式轉成后綴表達式時的括號也沒扔掉【無視就好】。
?
1 #include <iostream> 2 #include "stack.h" 3 using namespace std; 4 using namespace stack; 5 template class Stack<int>; 6 int main(void) 7 { 8 //(1.1*1.2+1.3*1.4)+(1.5*1.6^1.7^(1.8+1.9)) 9 calexp item[] = { ('('), (1.1), ('*'), (1.2), ('+'), (1.3), ('*'), (1.4), (')'), ('+'), ('('), (1.5), ('*'), (1.6), ('^'), (1.7), ('^'), ('('), (1.8), ('+'), (1.9), (')'), (')'), }; 10 infix_to_postfix(item, sizeof(item)/sizeof(item[0])); 11 for (int i = 0; i < sizeof(item) / sizeof(item[0]); ++i) 12 item[i].showcalexp(); 13 system("pause"); 14 } View Code?
(四)、實現代碼
該說的基本都說了,其它看注釋吧……………………
1 //練習3.20新增,中綴表達式轉后綴表達式,包含錯誤檢測 2 void infix_to_postfix(calexp item[], int size) 3 { 4 calexp* temp = new calexp[size]; 5 //初檢模塊 6 //排除不以數據或正括號開頭的表達式 7 if (item[0].gettype() != CALEXP_NUMBER && item[0].getopera() != '(') 8 { 9 cout << "Error: illegal beginning" << endl; 10 return; 11 } 12 //排除不以數據或反括號結尾的表達式 13 if (item[size - 1].gettype() != CALEXP_NUMBER && item[size - 1].getopera() != ')') 14 { 15 cout << "Error: illegal ending" << endl; 16 return; 17 } 18 for (int i = 0; i != size - 1; ++i) 19 { 20 //排除非法操作符 21 if (item[i].gettype() == CALEXP_OPERATOR && item[i].getlevel() == 0) 22 { 23 cout << "Error: illegal operator" << endl; 24 return; 25 } 26 //排除連續的數據 27 //排除連續的操作符(除非至少有一個是正括號或反括號) 28 if (item[i].gettype() == item[i + 1].gettype()) 29 { 30 if (item[i].gettype() == CALEXP_NUMBER) 31 { 32 cout << "Error: consecutive number" << endl; 33 return; 34 } 35 else 36 { 37 if ((item[i].getlevel() != 4 && item[i].getlevel() != -1) && (item[i + 1].getlevel() != 4 && item[i + 1].getlevel() != -1)) 38 { 39 cout << "Error: consecutive operators" << endl; 40 return; 41 } 42 } 43 } 44 } 45 //正式轉換 46 Stack<calexp> operators; 47 operators.push(calexp('!')); 48 int j = 0; 49 for (int i = 0; i != size; ++i) 50 { 51 //元素為數值時,直接加入臨時數組 52 if (item[i].gettype() == CALEXP_NUMBER) 53 temp[j++] = item[i]; 54 //元素為操作符時,進行相應的操作 55 else 56 { 57 //如果即將加入的操作符等級比棧頂操作符等級更高 58 //則無條件壓入棧 59 if (item[i].getlevel() > operators.getfirst().getlevel()) 60 operators.push(item[i]); 61 //如果即將加入的操作符等級比棧頂操作符等級更低或一致 62 else 63 { 64 //如果即將加入反括號 65 if (item[i].getlevel() == -1) 66 { 67 temp[j++] = item[i]; 68 //無條件彈出操作符直到最近一個正括號被彈出 69 while (operators.getfirst().getlevel() != 0 && operators.getfirst().getlevel() != 4) 70 temp[j++] = operators.getpop(); 71 if (operators.getfirst().getlevel() == 4) 72 temp[j++] = operators.getpop(); 73 //如果在棧中未發現正括號則報錯 74 else 75 { 76 cout << "Error: ( not found before )" << endl; 77 return; 78 } 79 } 80 //如果即將加入左結合的操作符(+,-,*,/) 81 else if (item[i].getlevel() == 1 || item[i].getlevel() == 2) 82 { 83 //那么彈出操作符直到空棧或發現正括號為止 84 while (operators.getfirst().getlevel() != 0 && operators.getfirst().getlevel() != 4) 85 temp[j++] = operators.getpop(); 86 //把當前操作符壓入 87 operators.push(item[i]); 88 } 89 //假如是左括號是右結合取冪操作符,無條件壓入且暫時不取出 90 else 91 operators.push(item[i]); 92 } 93 } 94 } 95 //原表達式遍歷完畢后,彈出操作符棧剩余符號 96 while (operators.getfirst().getlevel() != 0) 97 { 98 //如果符號中殘留正括號則報錯 99 if (operators.getfirst().getlevel() == 4) 100 { 101 cout << "Error: a ( without )" << endl; 102 return; 103 } 104 else 105 temp[j++] = operators.getpop(); 106 } 107 //將臨時數組復制回原數組 108 for (int i = 0; i != size; ++i) 109 item[i] = temp[i]; 110 delete[] temp; 111 }?
轉載于:https://www.cnblogs.com/catnip/p/4352975.html
總結
以上是生活随笔為你收集整理的【Weiss】【第03章】练习3.20:中缀表达式转后缀表达式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 时间记忆要有记忆
- 下一篇: Log4j MDC Tomcat下报异常