需求分析
四则运算表达式的解析和求值从概念上看是一个逻辑很清晰的过程。
遵循了左结合、优先级差异和括号可以提升优先级这三条规则。
实现思路
实际上只要遍历表达式,将小数和运算符分离到两个序列容器中,在遍历的过程中,考虑左结合和括号对优先级的影响,将相关信息和运算符记录在一起,然后在遍历结束后,按照优先顺序从高到底,从小数序列中拉取两个元素(可能是中间结果的二叉树,实际上小数是一种特例,是左右子树为空的解析完成的二叉树),并且把运算符的两棵子树指向两个元素,把这棵新树放回到小数容器中。不断重复这个过程直到运算符序列为空,这时小数序列中就只剩下单一的二叉树根节点,它是完整的解析结果。
对二叉树的求值相对容易,这里直接用节省代码的递归完成。
预处理
表达式的解析就不赘述了,它先于上述逻辑完成预处理的过程,就是纯粹的体力劳动。
这里用一个Token类实现主要的逻辑。主要是小数被解析成一个中间的Token结构,在其中保存float类型的小数数值。
Formula.h
#ifndef _FORMULA_H #define _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) { float sign = 1; p_cursor = ParsePureNum(p_cursor, p_num, sign); if (*p_cursor != ‘.‘) { // apply sign p_num *= sign; return p_cursor; } else { p_cursor++; float numPostDot = 0; p_cursor = ParsePureNum(p_cursor, numPostDot, sign, true); if (p_cursor) { p_num += numPostDot; // apply sign p_num *= sign; return p_cursor; } else { return NULL; } } } private: static const char* ParsePureNum(const char* p_cursor, float& p_num, float& p_sign, bool bPostDot = false) { bool bPermitSign = !bPostDot; p_sign = 1; 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; } // record sign if (bPermitSign && (ch == ‘-‘ || ch == ‘+‘)) { --i; switch(ch) { case ‘-‘: p_sign = -1; break; case ‘+‘: p_sign = 1; break; } continue; } bPermitSign = false; // 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 ‘+‘: if ( *(p_cursor+1) >= ‘0‘ && *(p_cursor+1) <= ‘9‘ ) { p_token.m_eToken = eTokenNum; return FloatParser::Parse(p_cursor, p_token.m_num); } else { p_token.m_eToken = eTokenPlus; break; } case ‘-‘: if ( *(p_cursor+1) >= ‘0‘ && *(p_cursor+1) <= ‘9‘ ) { p_token.m_eToken = eTokenNum; return FloatParser::Parse(p_cursor, p_token.m_num); } else { 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; }; #endif
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: "; char buffer[512]; cin.getline(buffer,sizeof(buffer)); string str(buffer); 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; } } }
时间: 2024-10-13 17:08:08