1_簡易計算器程式設計
介面展示
核心概念
二階構造
中綴表示式數字和運算子的分離演算法
QQueue<QString> QCalculatorDec::split(const QString& exp) { QQueue<QString> ret; QString num = ""; QString pre = ""; for(int i=0; i<exp.length(); i++) { if( isDigitOrDot(exp[i]) ) { num += exp[i]; pre = exp[i]; } else if( isSymbol(exp[i]) ) { if( !num.isEmpty() ) { ret.enqueue(num); num.clear(); } if( isSign(exp[i]) && ( (pre == "") || (pre == "(") || (isOperator(pre)) ) ) { num += exp[i]; } else { ret.enqueue(exp[i]); } pre = exp[i]; } } if( !num.isEmpty() ) { ret.enqueue(num); } return ret; }
-
分析,所要計算的中綴表示式中包含:
- 數字和小數點 【 0 - 9 活 . 】
- 符號位【 + 或 - 】
- 運算子【 +, -, *, / 】
- 括號 【 ( 或 ) 】
9.3 + ( 3 - -0.11 ) * 5
-
思想,以符號作為標記對錶達式中的字元逐個訪問
- 定義累計變數 num
-
當前字元 exp[i] 為陣列或小數點時:
- 累計: num += exp[i]
-
當前字元 exp[i] 為符號時:
- num 為運算數,分離並儲存
-
若 exp[i] 為正負號:
- 累計符號位 + 和 - : num += exp[i]
-
若 exp[i] w為運算子
- 分離並儲存
中綴表示式轉字尾表示式
bool QCalculatorDec::match(const QQueue<QString>& exp) { bool ret = true; QStack<QString> stack; for(int i=0; ret && (i<exp.length()); i++) { if( isLeft(exp[i]) ) { stack.push(exp[i]); } else if( isRight(exp[i]) ) { if( !stack.isEmpty() && isLeft(stack.top()) ) { stack.pop(); } else { ret = false; } } } return ret && stack.isEmpty(); }
- 確保表示式中的括號能夠左右匹配(括號成對出現;左括號先於右括號出現)
bool QCalculatorDec::transform(QQueue<QString>& exp, QQueue<QString>& output) { bool ret = match(exp); QStack<QString> stack; output.clear(); while ( ret && !exp.isEmpty() ) { QString e = exp.dequeue(); if( isNumber(e) ) { output.enqueue(e); } else if( isOperator(e) ) { while( (!stack.isEmpty()) && (priority(e) <= priority(stack.top())) ) { output.enqueue(stack.pop()); } stack.push(e); } else if( isLeft(e) ) { stack.push(e); } else if( isRight(e) ) { while( !stack.isEmpty() && !isLeft(stack.top()) ) { output.enqueue(stack.pop()); } if( !stack.isEmpty() ) { stack.pop(); } } else { ret = false; } } while( !stack.isEmpty() ) { output.enqueue(stack.pop()); } if( !ret ) { output.clear(); } return ret; }
-
分析,中綴表示式轉字尾表示式的過程類似編譯過程:
- 四則運算表示式中的括號必須匹配
- 根據運算子優先順序進行轉換
- 轉換後的表達時中沒有括號
- 轉換後可以順序的計算出最終結果
-
思想
- 當前元素 e 為數字:輸出
-
當前元素 e 為運算子:
- 1.與棧頂元素進行優先順序比較
- 2.小於等於,講棧頂元素輸出,轉 1
- 3.大於,講當前元素 e 入棧
- 當前元素 e 為左括號: 入棧
-
當前元素 e 為右括號:
- 1.彈出棧頂元素並輸出,直至棧頂元素為左括號
- 2.將棧頂的左括號從棧中彈出
9.3 + ( 3 - -0.11 ) * 5==>9.3 3 -0.11 - 5 * +
字尾表示式計算結果
-
思想
-
遍歷字尾表達時中的數字和運算子
- 當前元素為陣列,進棧
-
當前元素為運算子:
- 從棧中彈出右操作符
- 從棧中彈出左運算元
- 根據符號進行運算
- 將運算結果壓入棧中
-
遍歷結束
- 棧中的唯一數字為運算結果
-
QString QCalculatorDec::calculator(QString l, QString op, QString r) { QString ret = "Error"; if( isNumber(l) && isNumber(r) ) { double lp = l.toDouble(); double rp = r.toDouble(); if( op == "+" ) { ret.sprintf("%f", lp + rp); } else if( op == "-" ) { ret.sprintf("%f", lp - rp); } else if( op == "*" ) { ret.sprintf("%f", lp * rp); } else if( op == "/" ) { const double p = 0.00000000001; if( (-p < rp) && (rp < p) ) { ret.sprintf("%f", lp / rp); } } } return ret; } QString QCalculatorDec::calculator(QQueue<QString>& exp) { QString ret = "Error"; QStack<QString> stack; while( !exp.isEmpty() ) { QString e = exp.dequeue(); if( isNumber(e) ) { stack.push(e); } else if( isOperator(e) ) { QString r = !stack.isEmpty() ? stack.pop() : ""; QString l = !stack.isEmpty() ? stack.pop() : ""; QString result = calculator(l, e, r); if( result != "Error" ) { stack.push(result); } else { break; } } else { break; } } if( exp.isEmpty() && (stack.size() == 1) && isNumber(stack.top()) ) { ret = stack.pop(); } return ret; }
-
注意:
- 與數學相關的演算法需要考慮除 0 的情況
- 若是浮點運算,避免程式碼中直接與 0 做相等比較
使用者介面與互動邏輯分離
純虛類 - 介面
定義介面類:
class ICalculator { public: virtual bool expression(const QString& exp) = 0; virtual QString result() = 0; };
互動邏輯實現介面(QCalculatorDec):
class QCalculatorDec : public ICalculator { protected: // ... public: QCalculatorDec(); bool expression(const QString& exp); QString result(); ~QCalculatorDec(); };
使用者介面使用介面(QCalculatorUI):
class QCalculatorUI : public QWidget { Q_OBJECT private: // ... public: static QCalculatorUI* NewInstance(); void setCalculator(ICalculator* cal); ICalculator* getCalculator(); ~QCalculatorUI(); };
-
模組之間需要進行解耦(強內聚,弱耦合)
- 每個模組只實現單一的功能
- 模組內部的子模組只為整體的單一功能而存在
- 模組之間通過約定好的介面進行互動
- 模組間不能出現迴圈依賴
使用者介面與業務邏輯的互動:
計算機應用程式的整體框架:
總結
- GUI 應用程式中,需要進行使用者介面與業務邏輯的分離,在C++中使用純虛類實現介面。
- 為保證得到合法的物件,二階構造設計模式的使用是非常有必要的。
以上內容參考狄泰軟體學院系列課程,請大家保護原創!