需求分析
ios
四則運算表達式的解析和求值從概念上看是一個邏輯很清晰的過程。git
遵循了左結合、優先級差別和括號能夠提高優先級這三條規則。express
實現思路
c#
實際上只要遍歷表達式,將小數和運算符分離到兩個序列容器中,在遍歷的過程當中,考慮左結合和括號對優先級的影響,將相關信息和運算符記錄在一塊兒,而後在遍歷結束後,按照優先順序從高到底,從小數序列中拉取兩個元素(多是中間結果的二叉樹,實際上小數是一種特例,是左右子樹爲空的解析完成的二叉樹),而且把運算符的兩棵子樹指向兩個元素,把這棵新樹放回到小數容器中。不斷重複這個過程直到運算符序列爲空,這時小數序列中就只剩下單一的二叉樹根節點,它是完整的解析結果。post
對二叉樹的求值相對容易,這裏直接用節省代碼的遞歸完成。lua
預處理spa
表達式的解析就不贅述了,它先於上述邏輯完成預處理的過程,就是純粹的體力勞動。code
這裏用一個Token類實現主要的邏輯。主要是小數被解析成一箇中間的Token結構,在其中保存float類型的小數數值。orm
Formula.h遞歸
#include <iostream> #include <vector> #include <queue> #include <stack> #include <list> #include <algorithm> using namespace std; class FloatParser { public: static const char* Parse(const char* p_cursor, float& p_num) { p_cursor = ParsePureNum(p_cursor, p_num); if (*p_cursor != '.') { return p_cursor; } else { p_cursor++; float numPostDot = 0; p_cursor = ParsePureNum(p_cursor, numPostDot, true); if (p_cursor) { p_num += numPostDot; return p_cursor; } else { return NULL; } } } private: static const char* ParsePureNum(const char* p_cursor, float& p_num, bool bPostDot = false) { int i; for ( i = 0, p_num = 0; ; p_cursor++, i++ ) { char ch = *p_cursor; // reaches end of input EOF if (ch == 0) { if ( i == 0) return NULL; else break; } // skip all spaces, leading and traling if (ch == ' ') { --i; continue; } // accumulate digits if (ch >= '0' && ch <= '9') { p_num = 10 * p_num + (ch - '0'); continue; } // other characters, including dot, stop parsing break; } // if post dot digits, parse 9999 into 0.9999, where i is effective number of digits, in 9999 case namely 4 if (bPostDot) { for(; i > 0; i--) { p_num /= 10; } } return p_cursor; } }; enum EToken { eTokenNone, eTokenNum, eTokenPlus, eTokenMinus, eTokenMul, eTokenDiv, eTokenParLeft, eTokenParRight, }; class Token { public: Token(){} Token(float p_num):m_eToken(eTokenNum), m_num(p_num){} public: static const char* ParseOne(const char* p_cursor, Token& p_token) { start: switch (*p_cursor) { case ' ': do { ++p_cursor; }while(*p_cursor == ' '); goto start; case 0: return NULL; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': p_token.m_eToken = eTokenNum; return FloatParser::Parse(p_cursor, p_token.m_num); case '+': p_token.m_eToken = eTokenPlus; break; case '-': p_token.m_eToken = eTokenMinus; break; case '*': p_token.m_eToken = eTokenMul; break; case '/': p_token.m_eToken = eTokenDiv; break; case '(': p_token.m_eToken = eTokenParLeft; break; case ')': p_token.m_eToken = eTokenParRight; break; default: return NULL; } return ++p_cursor; } public: EToken m_eToken; float m_num; }; //int testFloatParser() //{ // float num; // FloatParser::Parse(".9090",num); // cout << num << endl; //} class Tree { public: char GetOpChar()const { switch(m_eToken) { case eTokenPlus: return '+'; case eTokenMinus: return '-'; case eTokenMul: return '*'; case eTokenDiv: return '/'; } return 'x'; } private: typedef float (*FUNC)(float op1, float op2); public: Tree(): m_eToken(eTokenNone), m_pLeft(NULL), m_pRight(NULL), m_num(0), m_level(0), m_indexOnLevel(0) { } float Evaluate() { cout << "Evaluating " << GetOpChar() << ":" << m_num << endl; switch(m_eToken) { case eTokenNum: return m_num; case eTokenPlus: case eTokenMinus: case eTokenMul: case eTokenDiv: return (Tree::GetFunc(m_eToken))(m_pLeft->Evaluate(), m_pRight->Evaluate()); default: return -3.14; } } public: int GetOpPriorty()const { switch(m_eToken) { case eTokenPlus: case eTokenMinus: return 0; case eTokenMul: case eTokenDiv: return 1; } return -1; } static Tree* MakeTreeByToken(Token token) { Tree * tree = new Tree; tree->m_eToken = token.m_eToken; tree->m_num = token.m_num; return tree; } static Tree* MakeTreeWithTokens(const vector<Token> tokens); private: static float funcPlus(float op1, float op2) { cout << op1 << "+" << op2 << endl; return op1 + op2; } static float funcMinus(float op1, float op2) { cout << op1 << "-" << op2 << endl; return op1 - op2; } static float funcMul(float op1, float op2) { cout << op1 << "*" << op2 << endl; return op1 * op2; } static float funcDiv(float op1, float op2) { cout << op1 << "/" << op2 << endl; return op1 / op2; } static FUNC GetFunc(EToken eToken) { switch(eToken) { case eTokenPlus: return funcPlus; case eTokenMinus: return funcMinus; case eTokenMul: return funcMul; case eTokenDiv: return funcDiv; default: return NULL; } } private: EToken m_eToken; float m_num; Tree *m_pLeft; Tree *m_pRight; int m_level; int m_indexOnLevel; friend class BiggerBetter; };
Formula.cpp
#include "Formula.h" struct BiggerBetter { bool operator()(Tree* op1, Tree* op2) { int nLevelDiff = op1->m_level - op2->m_level; int nOpDiff = op1->GetOpPriorty() - op2->GetOpPriorty(); int nIndexDiff = -(op1->m_indexOnLevel - op2->m_indexOnLevel); if (nLevelDiff != 0) return nLevelDiff < 0; if (nOpDiff != 0) return nOpDiff < 0; if (nIndexDiff != 0) return nIndexDiff < 0; return false; } }; Tree* Tree::MakeTreeWithTokens(const vector<Token> tokens) { vector<Tree*> trees; vector<Tree*> ops; priority_queue<Tree*, vector<Tree*>, BiggerBetter> opsPriority; stack<int> indexs; int level = 0; int indexOnLevel = 0; for(int i = 0; i < tokens.size(); i++) { const Token& token = tokens[i]; Tree* tree = Tree::MakeTreeByToken(token); switch(tree->m_eToken) { case eTokenNum: // push num into list trees.push_back(tree); break; case eTokenPlus: case eTokenMinus: case eTokenMul: case eTokenDiv: tree->m_level = level; tree->m_indexOnLevel = indexOnLevel++; // push op into list ops.push_back(tree); // push op into priority list opsPriority.push(tree); break; case eTokenParLeft: level++; indexs.push(indexOnLevel); indexOnLevel = 0; break; case eTokenParRight: level--; if(indexs.empty()) { for(int i = 0; i < trees.size(); i++) delete trees[i]; for(int i = 0; i < ops.size(); i++) delete ops[i]; // Parse Fail return NULL return NULL; } indexOnLevel = indexs.top(); indexs.pop(); break; } } while(!opsPriority.empty()) { // get opTree with highest priority, remove it from priority queue Tree * opTree = opsPriority.top(); cout << "opTree: " << opTree->GetOpChar() << endl; opsPriority.pop(); // index the opTree in trees, remove opTree from ops vector vector<Tree*>::iterator it = find(ops.begin(), ops.end(), opTree); int opIndex = it - ops.begin(); //remove(ops.begin(), ops.end(), opTree); ops.erase(it); // get tree1 and tree2 by opIndex Tree *tree1 = trees[opIndex]; Tree *tree2 = trees[opIndex+1]; // set tree1 and tree2 to left and rigth children of opTree opTree->m_pLeft = tree1; opTree->m_pRight = tree2; // insert opTree to tree, before tree1's position vector<Tree*>::iterator insertIt = find(trees.begin(), trees.end(), tree1); int nInsertPos = insertIt - trees.begin(); trees.insert(insertIt, opTree); // remove tree1 and tree2 from trees vector trees.erase(trees.begin() + nInsertPos+1); trees.erase(trees.begin() + nInsertPos+1); //remove(trees.begin(), trees.end(), tree1); //remove(trees.begin(), trees.end(), tree2); } // return the finished tree root cout << trees.size() << endl; return trees[0]; }
main.cpp
#include "Formula.h" #include <cstring> int main() { while(true){ cout << "Input the expression: "; string str; cin >> str; vector<Token> tokens; Token token; //const char* input = "(1+2)*(3.14-4)/(5-6-7-8-9*90)"; const char* input = str.c_str(); while(input && *input) { input = Token::ParseOne(input, token); if (!input) break; tokens.push_back(token); cout << "token:" << token.m_eToken << endl; } if (!input) { cout << "Parse broken" << endl; } else { cout << "Parse finished" << endl; // create formula tree with tokens Tree* tree = Tree::MakeTreeWithTokens(tokens); float num = tree->Evaluate(); cout << num << endl; } } }