标签:
需求分析
四则运算表达式的解析和求值从概念上看是一个逻辑很清晰的过程。
遵循了左结合、优先级差异和括号可以提升优先级这三条规则。
实现思路
实际上只要遍历表达式,将小数和运算符分离到两个序列容器中,在遍历的过程中,考虑左结合和括号对优先级的影响,将相关信息和运算符记录在一起,然后在遍历结束后,按照优先顺序从高到底,从小数序列中拉取两个元素(可能是中间结果的二叉树,实际上小数是一种特例,是左右子树为空的解析完成的二叉树),并且把运算符的两棵子树指向两个元素,把这棵新树放回到小数容器中。不断重复这个过程直到运算符序列为空,这时小数序列中就只剩下单一的二叉树根节点,它是完整的解析结果。
对二叉树的求值相对容易,这里直接用节省代码的递归完成。
预处理
表达式的解析就不赘述了,它先于上述逻辑完成预处理的过程,就是纯粹的体力劳动。
这里用一个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;
}
}
}
标签:
原文地址:http://my.oschina.net/u/1179554/blog/375437