上篇寫了MFC界面搭建,這篇就寫實現計算。涉及到數據結構,對新手很不友好。html
雖然是MFC程序,可是能靈活地分離後臺代碼,自行構建控制檯程序。數組
上篇文章連接:C++作四則運算的MFC計算器(一)MFC界面建立 數據結構
概要:ide
中綴表達式與後綴表達式函數
中綴:(60-20)/(5-1)。小學就學的東西post
後綴:60 20 – 5 1 - /,爲增長可讀性,以「#」作分隔符,60#20#-#5#1#-#/。編碼
後綴計算:從左到右遇到符號就計算符號左邊的兩個數並寫上新值,即優先的運算符相對在左。上例中遇到第一個‘-’算60-20得40,遇到第二個「-」計算5-1得4,遇到「/」計算30/3的10,結果是10。url
對比:中綴式人看起來方便,後綴式沒有括號,計算順序從前到後,計算機操做起來簡單。spa
棧的相關實現3d
輸入的值都是字符,因此須要一個字符結構的棧;在計算數時就須要一個處理數字結構的棧。
字符和數值的棧結構體分別命名SqStack和SqStackN。
而後實現棧初始化、進棧、出棧等棧操做函數。
在工程中,同時把棧結構體和操做函數聲明在新建的頭文件xxx.h中,或者工程中其餘頭文件如stdafx.h,函數體的實如今新建的xxx.cpp中,其餘cpp。
1 struct SqStack { 2 char data[maxsize]; 3 int top; 4 }; 5 struct SqStackN { 6 double data[maxsizen]; 7 int top; 8 }; 9 //棧操做函數—字符 10 void initStack(SqStack *&s); 11 bool Push(SqStack *&s, char e); 12 bool Pop(SqStack *&s, char &e); 13 bool GetTop(SqStack *s, char &e); 14 void DestroyStack(SqStack *&s); 15 bool StackEmpty(SqStack *s); 16 //棧操做函數—數字 17 void initStack(SqStackN *&s); 18 bool Push(SqStackN *&s, double e); 19 bool Pop(SqStackN *&s, double &e); 20 bool GetTop(SqStackN *s, double &e); 21 void DestroyStack(SqStackN *&s); 22 bool StackEmpty(SqStackN *s); 23 24 //後綴表達式轉換函數 25 void trans(char* exp, char postexp[]); 26 //計算後綴表達式函數 27 double calculate(char* postexp);
1 void initStack(SqStack *&s) { 2 s = new SqStack(); 3 s->top = -1; 4 } 5 bool Push(SqStack *&s, char e) { 6 if (s->top == maxsize-1) 7 return false; 8 s->top++; 9 s->data[s->top] = e; 10 return true; 11 } 12 bool Pop(SqStack *&s, char &e) { 13 if (s->top == -1) 14 return false; 15 e = s->data[s->top]; 16 s->top--; 17 return true; 18 } 19 bool GetTop(SqStack *s, char &e) { 20 if (s->top == -1) 21 return false; 22 e = s->data[s->top]; 23 return true; 24 } 25 void DestroyStack(SqStack *&s) { 26 free(s); 27 } 28 bool StackEmpty(SqStack *s) { 29 return (s->top == -1); 30 } 31 32 void initStack(SqStackN *& s){ 33 s = new SqStackN(); 34 s->top = -1; 35 } 36 37 bool Push(SqStackN *& s, double e) 38 { 39 if (s->top == maxsizen - 1) 40 return false; 41 s->top++; 42 s->data[s->top] = e; 43 return true; 44 } 45 46 bool Pop(SqStackN *& s, double & e) 47 { 48 if (s->top == -1) 49 return false; 50 e = s->data[s->top]; 51 s->top--; 52 return true; 53 } 54 55 bool GetTop(SqStackN * s, double & e) 56 { 57 if (s->top == -1) 58 return false; 59 e = s->data[s->top]; 60 return true; 61 } 62 63 void DestroyStack(SqStackN *& s){ 64 free(s); 65 } 66 67 bool StackEmpty(SqStackN * s) 68 { 69 return (s->top == -1); 70 }
用棧將中綴表達式轉換成後綴表達式
從左向右掃描中綴,遇到數字就添加到後綴中,遇到運算符進行棧處理,而棧的處理依賴於運算符優先級,優先級高的靠近棧頂。結束後後綴式還是字符數組。
寫個函數tans(),有2個參數char * exp和char postexp[ ],
先初始化一個字符棧指針s,char e用來操做棧頂元素,int p做爲postexp數組的下表。寫個循環掃描exp
while (*exp != '\0') {switch(*exp){case:case:case:default}......}
掃描到數字字符時直接加到後綴式中,並加上 ‘ # ’ 以分割。
其餘狀況無非是+、-、*、/、(、)這6個符號。
(、)優先級最高,在括號之間的運算符必定比括號以外的優先運算,遇到 ‘ ( ’ 即進棧,遇 ‘ ) ’ 即出棧棧頂元素直到出來的是 ‘ ( ’ 。所以棧中不會有 ‘ ) ’ 。
*、/優先級其次,先判斷棧頂是什麼,棧頂是*、/則將其出棧值後綴式,棧頂是+、-則將 ‘ * ’ 或 ‘ / ’ 進棧。
+、-優先級最低,這是棧頂元素無論是+-*/都出棧至後綴式,但棧頂是 ‘ ( ’ 時就不須要出棧,‘ ( ’能夠看做是一個新的運算起點,將+或-進棧便可。
exp掃描後棧可能還會有運算符,將剩下的都出棧至後綴式。再爲後綴式加結束標識 ‘\0’ ,銷燬棧釋放空間。
1 void trans(char* exp,char postexp[]) { 2 char e; 3 SqStack *s; initStack(s); // 即SqStack s = new SqStacck()
4 int p = 0;//postexp的下表
5 while (*exp != '\0') { 6 switch (*exp) 7 { 8 case '+': 9 case '-': 10 while (!StackEmpty(s)) { 11 GetTop(s, e); 12 if (e != '(') { 13 postexp[p++] = e; 14 Pop(s, e); 15 } 16 else
17 break; 18 } 19 Push(s, *exp); 20 exp++;break; 21 case '*': 22 case '/': 23 while (!StackEmpty(s)) { 24 GetTop(s, e); 25 if (e == '*' || e == '/') { 26 postexp[p++] = e; 27 Pop(s, e); 28 } 29 else
30 break; 31 } 32 Push(s, *exp); 33 exp++;break; 34 case '(': 35 Push(s, *exp); 36 exp++;break; 37 case ')': 38 Pop(s, e); 39 while (e != '(') { 40 postexp[p++] = e; 41 Pop(s, e); 42 } 43 exp++;break; 44 default: 45 while (*exp >= '0'&&*exp <= '9') { 46 postexp[p++] = *exp; 47 exp++; 48 } 49 postexp[p++] = '#'; 50 break; 51 } 52 } 53 while (!StackEmpty(s)) { 54 Pop(s, e); 55 postexp[p++] = e; 56 } 57 postexp[p] = '\0'; 58 DestroyStack(s); 59 }
棧計算後綴表達式
這一功能相對簡單些,掃描後綴式,
遇到表示數字的字符時,例如 ‘ 6 ’ ,利用它與 ‘ 0 ’ 編碼之間的差獲得數字,‘ 6 ’ - ‘ 0 ’ = 6 ,乘10實現位數值。獲得的數值放入棧裏。
遇到運算符時直接出棧兩個元素,此時這兩個元素必定是數字,第一個出棧的作運算符右值,第二個作左值,順序不能反,結果還要入棧以進行下一步運算。
遇到 ‘ / ’ 時還要判斷棧頂是不是0,被除數可不能是0啊。
最後棧中就是運算結果,出棧做爲返回值。
1 double calculate(char* postexp) { 2 double a, b,c,d,e; 3 SqStackN *o; initStack(o); 4 while (*postexp != '\0') { 5 switch (*postexp) 6 { 7 case '+': 8 Pop(o, a); 9 Pop(o, b); 10 c = b + a; 11 Push(o, c); 12 break; 13 case '-': 14 Pop(o, a); 15 Pop(o, b); 16 c = b - a; 17 Push(o, c); 18 break; 19 case '*': 20 Pop(o, a); 21 Pop(o, b); 22 c = b * a; 23 Push(o, c); 24 break; 25 case '/': 26 Pop(o, a); 27 Pop(o, b); 28 if (a != 0) { 29 c = b / a; 30 Push(o, c); 31 break; 32 } 33 else { 34 exit(0); 35 } 36 break; 37 default: 38 d = 0; 39 while (*postexp >= '0'&&*postexp <= '9') { 40 d = d * 10 + (*postexp - '0'); 41 postexp++; 42 } 43 Push(o, d); 44 break; 45 } 46 postexp++; 47 } 48 GetTop(o, e); 49 DestroyStack(o); 50 return e; 51 }
等號按鈕功能-計算結果顯示
運算功能已經實現了,可是要講功能綁定到 ‘ = ’ 還有最後一道坎~
MFC文本編輯框的類型是CString,要想轉換成char[]有點讓人頭大。
須要幾個變量,輸入的算式char exp[50],後綴式char postexp[50],運算結果result。
CString轉換爲char[]直接上代碼吧: ::wsprintfA(exp, "%ls", (LPCTSTR)editv);
double轉CString代碼: resultv.Format(_T("%.5lf"), result);
1 void CMFCcalculationDlg::OnBnClickedButton19() 2 { 3 // TODO: 等號按鈕
4 char exp[50]; 5 char postexp[50]; 6 double result; 7 UpdateData(true); 8 ::wsprintfA(exp, "%ls", (LPCTSTR)editv); 9 trans(exp, postexp); 10 result = calculate(postexp); 11 resultv.Format(_T("%.5lf"), result); 12 //resultv.Format(TEXT("%lf\n%.2lf"), result);
13 UpdateData(false); 14 }
運行結果:
程序在未發佈前比較大,100多M,包含了不少不用的文件,在資源管理器裏仍是隱藏的。發佈後的程序只有幾M。
程序發佈:將Debug改爲Release,運行即發佈,以後項目同級目錄裏有Release文件夾,裏面就是你的應用程序。