專案——簡易計算器
2017.12.21
決定做一個小專案來練練手了,對期末考試感到無所畏懼。
先選擇簡易計算器吧,核心演算法中綴轉字尾表示式我還是學過的,最起碼能克服一點心裡畏懼。
專案預期如下:
- 實現命令列版本的核心演算法,做簡單的加減乘除就可以了;
- 實現圖形化視窗。
程式碼預期要200行以上.
對自己提一個要求:可以上網找思路,但是絕對不看別人的原始碼。
2017.12.23
經過幾天的努力,總算把這個計算器的核心程式碼寫出來了。程式碼接近200行,主要利用二重字串來實現核心的逆波蘭演算法,實在是有點繁雜。
這段程式碼實在不堪一看,各種結構體、變數、函式的命名隨心所欲,特別是我在操作棧的時候沒有專門寫幾個函式來執行,因為實在不好分類,顯得有點繁雜。這也說明了我這種思路侷限性太大,程式碼複用率不高。
但畢竟是自己做出來的,發上來見證一下。編輯器用的是VS2017,有些函式會有些奇怪。另外,結果是浮點型,要保證在6位數以內才絕對正確,不然會有取捨。
// 簡易計算器.cpp: 定義控制檯應用程式的入口點。 // 2017.12.23 #include "stdafx.h" #include <iostream> #include <cstdio> #include <conio.h> #include <cstdlib> using namespace std; #define MaxSize 100 // 定義資料棧,儲存轉化為字尾表示式的字串算式 typedef struct Data_SNode { int Top; int Ptr; char Elem[MaxSize][MaxSize]; } *D_Stack; // 定義操作符棧,用於執行中綴轉字尾表示式 typedef struct Operation_SNode { int Top; char Elem[MaxSize]; } *Oper_Stack; // 定義算式棧,用於執行字尾表示式的計算 typedef struct Counter_SNode { int Top; double Elem[MaxSize]; } *C_Stack; D_Stack initData(); // 初始化資料棧 Oper_Stack initOper(); // 初始化操作符棧 C_Stack initCounter(); // 初始化算式棧 void getInput(D_Stack Data, Oper_Stack Oper); // 接受輸入算式並儲存在資料棧中 void PreToSuf(char ch, Oper_Stack Oper, D_Stack Data); // 操作符中綴轉字尾表示式演算法,即逆波蘭核心演算法 void Count(D_Stack Data, C_Stack Counter); // 計算字尾表示式 int main() { D_Stack Data = initData(); Oper_Stack Oper = initOper(); C_Stack Counter = initCounter(); getInput(Data, Oper); Count(Data, Counter); return 0; } D_Stack initData() { int i, j; D_Stack Data; Data = (struct Data_SNode *)malloc(sizeof(struct Data_SNode)); for (i = 0; i < MaxSize; i++) { for (j = 0; j < MaxSize; j++) { Data->Elem[i][j] = '\0'; } } Data->Top = -1; Data->Ptr = -1; return Data; } Oper_Stack initOper() { int i; Oper_Stack Oper; Oper = (struct Operation_SNode *)malloc(sizeof(struct Operation_SNode)); for (i = 0; i < MaxSize; i++) { Oper->Elem[i] = '\0'; } Oper->Top = -1; return Oper; } C_Stack initCounter() { int i; C_Stack Counter; Counter = (struct Counter_SNode *)malloc(sizeof(struct Counter_SNode)); for (i = 0; i < MaxSize; i++) { Counter->Elem[i] = 65535; } Counter->Top = -1; return Counter; } void getInput(D_Stack Data, Oper_Stack Oper) { int flag; // 標記之前的輸入是什麼 char ch; // 接收輸入字元 flag = 0; // flag:0是初始狀態,1代表上一次輸入數字,2代表上一次輸入符號 ch = _getche(); while (ch != 61) // 在輸入‘=’之前 { if (ch >= 48 && ch <= 57) // 如果輸入字元是數字,直接存入 { if( flag == 0) // 初始字元 { Data->Elem[++Data->Top][++Data->Ptr] = ch; flag = 1; } else if (flag == 1) // 前一個輸入為數字,繼續向後排 { Data->Elem[Data->Top][++Data->Ptr] = ch; } else // flag == 2 // 前一個輸入為操作符,換一行排 { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = ch; flag = 1; } } else if (ch == 42 || ch == 43 || ch == 45 || ch == 47) // 如果接受字元是符號,執行中綴轉字尾演算法 { PreToSuf(ch, Oper, Data); flag = 2; } ch = _getche(); } while (Oper->Top >= 0) // 輸入‘=’後,將操作符棧中的字元全部取出來 { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--]; } } void PreToSuf(char ch, Oper_Stack Oper, D_Stack Data) { if (Oper->Top == -1) // 如果棧空 { Oper->Elem[++Oper->Top] = ch; } else { if (ch == 42 || ch == 47) // 如果接受字元為*或/,則將棧頂的*或/全部排出再存入 { while (Oper->Elem[Oper->Top] == 42 || Oper->Elem[Oper->Top] == 47) { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--]; } Oper->Elem[++Oper->Top] = ch; } else if (ch == 43 || ch == 45) // 如果接受字元為+或-,則將棧清空再存入 { while (Oper->Top >= 0) { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--]; } Oper->Elem[++Oper->Top] = ch; } } } void Count(D_Stack Data, C_Stack Counter) { int i; double Pre, Later, RST; // Pre為前數,Later為後數,RST為結果值 char Operation; // 符號 for (i = 0; i <= Data->Top; i++) { if (Data->Elem[i][0] >= 48) { Counter->Elem[++Counter->Top] = atof(Data->Elem[i]); // atof()將字元型別轉化為double型別 } else { Operation = Data->Elem[i][0]; Later = Counter->Elem[Counter->Top--]; Pre = Counter->Elem[Counter->Top--]; switch (Operation) { case 43: RST = Pre + Later; break; // 加 case 45: RST = Pre - Later; break; // 減 case 42: RST = Pre * Later; break; // 乘 case 47: RST = Pre / Later; break; // 除 } Counter->Elem[++Counter->Top] = RST; } } cout << Counter->Elem[Counter->Top]; }
2017.12.24
本想今天做個介面的,看了一下EasyX的函式覺得好絕望,這要怎麼做,做出來估計程式碼也得重改過了……然後就四處瀏覽。
本打算放棄的,畢竟C/C++做圖形介面太難了。但是立下的flag怎麼能輕易放棄,所以還是繼續做吧。今天就是把小數點和括號的功能完善了,這個不是很難。
改動的就是下面兩個核心函式,其它的都照舊,挺水的就過去了。
另外,給平安夜還在學程式碼的自己打call,也給大家祝快。
void getInput(D_Stack Data, Oper_Stack Oper) { int flag; // 標記之前的輸入是什麼 char ch; // 接收輸入字元 flag = 0; // flag:0是初始狀態,1代表上一次輸入數字,2代表上一次輸入符號 ch = _getche(); while (ch != 61) // 在輸入‘=’之前 { if (ch >= 48 && ch <= 57 || ch == 46) // 如果輸入字元是數字或小數點,直接存入 { if( flag == 0) // 初始字元 { Data->Elem[++Data->Top][++Data->Ptr] = ch; flag = 1; } else if (flag == 1) // 前一個輸入為數字,繼續向後排 { Data->Elem[Data->Top][++Data->Ptr] = ch; } else // flag == 2 // 前一個輸入為操作符,換一行排 { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = ch; flag = 1; } } else if (ch == 42 || ch == 43 || ch == 45 || ch == 47 || ch == 40 || ch == 41) { // 如果接受字元是符號,執行中綴轉字尾演算法 PreToSuf(ch, Oper, Data); flag = 2; } ch = _getche(); } while (Oper->Top >= 0) // 輸入‘=’後,將操作符棧中的字元全部取出來 { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--]; } } void PreToSuf(char ch, Oper_Stack Oper, D_Stack Data) { if (Oper->Top == -1) // 如果棧空 { Oper->Elem[++Oper->Top] = ch; } else { if (ch == 42 || ch == 47) // 如果接受字元為*或/,則將棧頂的*或/全部排出再存入 { while (Oper->Elem[Oper->Top] == 42 || Oper->Elem[Oper->Top] == 47) { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--]; } Oper->Elem[++Oper->Top] = ch; } else if (ch == 43 || ch == 45) // 如果接受字元為+或- { if (Oper->Elem[Oper->Top] == 40) // 棧頂是(,則直接存入 Oper->Elem[++Oper->Top] = ch; else // 棧頂為其他,則取出至棧頂為(或棧空,再存入 { while (Oper->Top >= 0 && Oper->Elem[Oper->Top] != 40) { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--]; } Oper->Elem[++Oper->Top] = ch; } } else if (ch == 40) // 接受字元為(,則直接存入 { Oper->Elem[++Oper->Top] = ch; } else if (ch == 41) // 接受字元為),則將棧內(前字元全部排出,並捨棄( { while (Oper->Elem[Oper->Top] != 40) { Data->Ptr = -1; Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--]; } Oper->Top--; } } }
2017.12.25
上網專門找了別人是如何做圖形介面的,發現和我想象中的相去甚遠。看了下程式碼,發現要是想實現點選計算的功能,整個程式碼得重頭開始,但其實核心功能我已經實現了。如果只是想輸入計算,那麼我現在做的已經差不多達到目的了。再做下去就是如何完善細節,已經優化程式碼的事情了,所以乾脆放棄這個小任務。
想了想還是有點不爽,做了這幾天就完成這麼一個小玩意,實在沒達到目的。既然C/C++圖形介面做得這麼差勁,實在做不起自己心儀的小專案,那乾脆之後學Python吧。
附上找到的圖形介面版本的計算器:
http://blog.csdn.net/shu_lance/article/details/51570987