计算包含函数的算术表达式

       算术表达式可以使用中缀表达式和后缀表达式(逆波兰表达式)计算,以下分别实现这两个方法,以下实现方法可以在算术表达式中包含部分函数,如三角函数sin等。

        由于普通的算术操作符(如“+-*/”)只包含一个字符,而函数会包含多个字符,为了方便先将函数用一个FunctionId的枚举替代,该枚举从1开始,算术表达式一般为明文字符串,这些枚举数只要使用20以下的数字就不会发生冲突,只是打印出来会是乱码,计算不会受影响。

enum FunctionId
{
	None = 0,
	Sin = 1,
	Cos = 2,
	Tan = 3,
	Cot = 4,
	FunctionMax,
};

         然后定义函数和运算符的结构FunctionInfo和OperatorInfo,以及一些全局变量和方法。


struct FunctionInfo
{
	FunctionId funcId;
	char funcStr[10];//函数名,如"sin"
	int bytesOfStr;//函数名字节数,如"sin"为3字节
	FunctionInfo(FunctionId InFuncId, const char* InFuncStr, int InBytes = 0)
	{
		funcId = InFuncId;
		memset(funcStr, 0, 10);
		bytesOfStr = InBytes > 0 ? InBytes : strlen(InFuncStr);
		memcpy(funcStr, InFuncStr, bytesOfStr);
	}
	static FunctionInfo None;
};
FunctionInfo FunctionInfo::None = FunctionInfo(FunctionId::None, "", 0);

FunctionInfo g_functionInfos[] =
{
	FunctionInfo(FunctionId::Sin,   "sin",   3),
	FunctionInfo(FunctionId::Cos,   "cos",  3),
	FunctionInfo(FunctionId::Tan,   "tan", 3),
	FunctionInfo(FunctionId::Cot,   "cot", 3),
	FunctionInfo::None
};
//判断是否是函数
FunctionInfo GetFunctionInfo(const char* InExp)
{
	for (int i = 0; i < FunctionId::FunctionMax; i++)
	{
		if (g_functionInfos->funcId == FunctionId::None)
		{
			return FunctionInfo::None;
		}
		if (memcmp(InExp, g_functionInfos[i].funcStr, g_functionInfos[i].bytesOfStr) == 0)
		{
			return g_functionInfos[i];
		}
	}
	return FunctionInfo::None;
}

struct OperatorInfo
{
	char ch;//运算符
	int priority;//优先级
	OperatorInfo(char InChar, int InPriority)
	{
		ch = InChar;
		priority = InPriority;
	}
};
OperatorInfo g_operatorInfos[] =
{
	OperatorInfo('(',   0),
	OperatorInfo(')',   0),
	OperatorInfo('+',   1),
	OperatorInfo('-',   1),
	OperatorInfo('*',   2),
	OperatorInfo('/',   2),
	OperatorInfo('%',   2),
	OperatorInfo('^',   3),
	OperatorInfo('\0',  -1)
};
//判断是否为操作符
bool IsOperator(char c)
{
	for (int i = 0;; i++)
	{
		if (g_operatorInfos[i].ch == c)
		{
			return true;
		}
		if (g_operatorInfos[i].ch == '\0')
		{
			return false;
		}
	}
	return false;
}
//获取操作符优先级
int GetPriority(char c)
{
	//函数优先级最高
	if (c > FunctionId::None && c < FunctionId::FunctionMax)
	{
		return 10;
	}
	for (int i = 0;; i++)
	{
		if (g_operatorInfos[i].ch == c)
		{
			return g_operatorInfos[i].priority;
		}
		if (g_operatorInfos[i].ch == '\0')
		{
			return -1;
		}
	}
	return -1;
}

bool IsNumber(char c)
{
	return c >= '0' && c <= '9';
}
//是否为数字,包括小数点
bool IsNumberOrDot(char c)
{
	return IsNumber(c) || (c == '.');
}

1、中缀表达式计算

      中缀表达式需要一个操作符栈和操作数栈,多个字符的函数被替换为一个字节存放到操作符栈中,判断时只需判断是否大于None且小于FunctionMax,计算值时操作数只有一个。


double CalcValueByInfixExpression(const char* expression)
{
	std::stack<char> operatorStack;//操作符栈,包括普通运算符和函数
	std::stack<double> dataStack;//操作数栈

	const char* iterExp = expression;
	if (*iterExp == '-')//判断第一个符号是否为'-',如果是就按"0-..."处理
	{
		dataStack.push(0);
		operatorStack.push('-');
		iterExp++;
	}

	while (*iterExp != '\0')
	{
		if (*iterExp == ' ')
		{
			iterExp++;
			continue;
		}
		FunctionInfo funcInfo = GetFunctionInfo(iterExp);
		if (funcInfo.funcId > FunctionId::None)
		{
			//函数直接入栈
			operatorStack.push(funcInfo.funcId);
			iterExp += funcInfo.bytesOfStr;
		}
		else if (IsOperator(*iterExp))
		{
			char ch = *iterExp;
			if (ch == ')')
			{
				//遇到右括号,则弹出之前的运算符直到遇到左括号'(',并将计算数据保存到栈顶
				while (!operatorStack.empty() && operatorStack.top() != '(')
				{
					char topOper = operatorStack.top();
					if (topOper > FunctionId::None && topOper < FunctionId::FunctionMax)
					{
						//函数只有一个操作数,有多个操作数的函数不考虑
						double topData = dataStack.top();
						dataStack.top() = CalcFunctionValue(topData, (FunctionId)topOper);
					}
					else
					{
						//运算符有两个操作数,其他情况不考虑
						double rightData = dataStack.top();
						dataStack.pop();
						double leftData = dataStack.top();
						dataStack.top() = CalcOperatorValue(leftData, rightData, topOper);
					}
					operatorStack.pop();
				}
				operatorStack.pop();
				if (!operatorStack.empty())
				{
					char topOper = operatorStack.top();//函数后跟的都是'(',所以要判断一下下一个运算符是不是函数
					if (topOper > FunctionId::None && topOper < FunctionId::FunctionMax)
					{
						double topData = dataStack.top();
						dataStack.top() = CalcFunctionValue(topData, (FunctionId)topOper);
						operatorStack.pop();
					}
				}
			}
			else if (ch == '-' && *(iterExp - 1) == '(')
			{
				//'('后也可能跟负数,也当成"0-..."处理
				//不考虑'-'前有空格的情况,可提前将表达式的空格去掉
				dataStack.push(0);
				operatorStack.push('-');
			}
			else if (operatorStack.empty() || ch == '(' || GetPriority(ch) > GetPriority(operatorStack.top()))
			{
				//这三种情况直接将运算符入栈
				operatorStack.push(ch);
			}
			else
			{
				//优先级高于或等于ch优先级的出栈进行运算,最后将ch入栈
				while (!operatorStack.empty() && GetPriority(ch) <= GetPriority(operatorStack.top()))
				{
					char topOper = operatorStack.top();
					if (topOper > FunctionId::None && topOper < FunctionId::FunctionMax)
					{
						dataStack.top() = CalcFunctionValue(dataStack.top(), (FunctionId)topOper);
					}
					else
					{
						double rightData = dataStack.top();
						dataStack.pop();
						double leftData = dataStack.top();
						dataStack.top() = CalcOperatorValue(leftData, rightData, topOper);
					}
					operatorStack.pop();
				}
				operatorStack.push(ch);
			}
			iterExp++;
		}
		else if (IsNumberOrDot(*iterExp))
		{
			//操作数入栈
			char buf[20] = { 0 };
			char* iterDest = buf;
			while (IsNumberOrDot(*iterExp))
			{
				*iterDest++ = *iterExp++;
			}

			double opData = atof(buf);
			dataStack.push(opData);
		}
		else
		{
			return 0;//表达式有误
		}
	}

	//将操作符都出栈运算,这里不会有函数,因为函数后都跟'(',在'('出栈的时候已经计算了
	while (!operatorStack.empty())
	{
		double rightData = dataStack.top();
		dataStack.pop();
		double leftData = dataStack.top();
		dataStack.top() = CalcOperatorValue(leftData, rightData, operatorStack.top());
		operatorStack.pop();
	}
	return dataStack.top();
}

2、后缀表达式计算

      后缀表达式分为两步,先将中缀表达式转为后缀表达式,然后计算后缀表达式,运算符的处理上与中缀表达式有很多相似之处。


//InExpression为原表达式,OutSuffixExp为后缀表达式
bool ExpressionToSuffixExp(const char* InExpression, char* OutSuffixExp)
{
	std::stack<char> operatorStack;//操作符栈,包括普通运算符和函数
	const char* iterInfix = InExpression;
	char* iterSuffix = OutSuffixExp;
	if (*iterInfix == '-')//判断第一个符号是否为'-',如果是就按"0-..."处理
	{
		*iterSuffix++ = '0';
		*iterSuffix++ = ' ';
		operatorStack.push('-');
		iterInfix++;
	}
	while (*iterInfix != '\0')
	{
		if (*iterInfix == ' ')
		{
			iterInfix++;
			continue;
		}
		FunctionInfo funcInfo = GetFunctionInfo(iterInfix);
		if (funcInfo.funcId > FunctionId::None)
		{
			operatorStack.push(funcInfo.funcId);
			iterInfix += funcInfo.bytesOfStr;
		}
		else if (IsOperator(*iterInfix))
		{
			char ch = *iterInfix;
			if (ch == ')')
			{
				//遇到右括号,则弹出之前的运算符直到遇到左括号'(',并将运算符加入到后缀表达式
				while (!operatorStack.empty() && operatorStack.top() != '(')
				{
					*iterSuffix++ = operatorStack.top();
					operatorStack.pop();
				}
				if (!operatorStack.empty())
				{
					operatorStack.pop();
				}
			}
			else if (ch == '-' && *(iterInfix - 1) == '(')
			{
				//'('后也可能跟负数,也当成"0-..."处理
				//不考虑'-'前有空格的情况,可提前将表达式的空格去掉
				*iterSuffix++ = '0';
				*iterSuffix++ = ' ';
				operatorStack.push('-');
			}
			else if (operatorStack.empty() || ch == '(' || GetPriority(ch) > GetPriority(operatorStack.top()))
			{
				//这三种情况直接将运算符入栈
				operatorStack.push(ch);
			}
			else
			{
				//优先级高于或等于ch优先级的出栈,并加入后缀表达式,最后将ch入栈
				while (!operatorStack.empty() && GetPriority(ch) <= GetPriority(operatorStack.top()))
				{
					*iterSuffix++ = operatorStack.top();
					operatorStack.pop();
				}
				operatorStack.push(ch);
			}
			iterInfix++;
		}
		else if (IsNumberOrDot(*iterInfix))
		{
			while (IsNumberOrDot(*iterInfix))
			{
				*iterSuffix++ = *iterInfix++;
			}
			*iterSuffix++ = ' ';//用空格分隔操作数
		}
		else
		{
			return false;//表达式有误
		}
	}
	//将剩余操作符出栈加入后缀表达式
	while (!operatorStack.empty())
	{
		*iterSuffix++ = operatorStack.top();
		operatorStack.pop();
	}
	*iterSuffix = '\0';
	return true;
}

double CalcSuffixValue(const char* InSuffixExp)
{
	std::stack<double> dataStack;//操作数栈
	const char* iterExp = InSuffixExp;
	while (*iterExp != '\0')
	{
		if (*iterExp == ' ')
		{
			iterExp++;
			continue;
		}
		//计算后缀表达式只需要区分操作数和操作符即可
		if (IsNumberOrDot(*iterExp))
		{
			int t = 0;
			char buf[20];
			while (IsNumberOrDot(*iterExp))
			{
				buf[t++] = *iterExp++;
			}
			buf[t] = '\0';
			double d = atof(buf);
			dataStack.push(d);
		}
		else
		{
			if (*iterExp > FunctionId::None && *iterExp < FunctionId::FunctionMax)
			{
				double topData = dataStack.top();
				dataStack.top() = CalcFunctionValue(topData, (FunctionId)* iterExp);
			}
			else
			{
				double rightData = dataStack.top();
				dataStack.pop();
				double leftData = dataStack.top();
				dataStack.top() = CalcOperatorValue(leftData, rightData, *iterExp);
			}
			iterExp++;
		}
	}
	if (dataStack.empty())
	{
		return 0;//表达式有误才会进到这里
	}
	return dataStack.top();
}

double CalcValueBySuffixExpression(const char* expression)
{
	//分两步,先转为后缀表达式,后计算表达式
	char suffixExp[100] = { 0 };
	ExpressionToSuffixExp(expression, suffixExp);
	return CalcSuffixValue(suffixExp);
}

       由于算术运算符手动输入,会有各种各样的形式,甚至是输入错误的表达式,这里还有很多特殊情况没有考虑到,只处理一些比较简单的情况。另外如果要添加其他函数需在枚举(FunctionId)、全局数组g_functionInfos以及函数CalcFunctionValue中增加相关代码。

       最后使用控制台程序测试。

int main()
{
	char expression[100];
	printf("请输入表达式:\n");
	gets_s(expression, 100);

	printf("表达式计算结果为:\n");

	printf("CalcValueByInfixExpression:%f\n", CalcValueByInfixExpression(expression));

	printf("CalcValueBySuffixExpression:%f\n", CalcValueBySuffixExpression(expression));
	system("pause");
	return  0;
}

       输出结果:

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值