码迷,mamicode.com
首页 > 其他好文 > 详细

四则运算表达式解析和求值(支持括号、小数、小数正负号)

时间:2015-02-04 20:30:13      阅读:795      评论:0      收藏:0      [点我收藏+]

标签:


需求分析

四则运算表达式的解析和求值从概念上看是一个逻辑很清晰的过程。

遵循了左结合、优先级差异和括号可以提升优先级这三条规则。


实现思路

实际上只要遍历表达式,将小数和运算符分离到两个序列容器中,在遍历的过程中,考虑左结合和括号对优先级的影响,将相关信息和运算符记录在一起,然后在遍历结束后,按照优先顺序从高到底,从小数序列中拉取两个元素(可能是中间结果的二叉树,实际上小数是一种特例,是左右子树为空的解析完成的二叉树),并且把运算符的两棵子树指向两个元素,把这棵新树放回到小数容器中。不断重复这个过程直到运算符序列为空,这时小数序列中就只剩下单一的二叉树根节点,它是完整的解析结果。

对二叉树的求值相对容易,这里直接用节省代码的递归完成。


预处理

表达式的解析就不赘述了,它先于上述逻辑完成预处理的过程,就是纯粹的体力劳动。

这里用一个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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!