算术表达式求值(南航2022数据结构课设第二题)

[问题描述]

一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正实数,运算符只含加减乘除等四种运算符,界限符有左右括号和表达式起始、结束符“#”,如:#6+15*(21-8/4)#。引入表达式起始、结束符是为了方便。编程利用“运算符优先法”求算术表达式的值。

[基本要求]

(1)从键盘或文件读入一个合法的算术表达式,输出正确的结果。

(2)显示输入序列和栈的变化过程。

(3)考虑算法的健壮性,当表达式错误时,要给出错误原因的提示。

(4)实现非整数的处理(*)。

[基本思路]

有一说一,这道题的计算部分并不难,只要跟着书上的思路写就可以了,由于我偷懒,选择采用系统栈函数(主要是我一开始没看见需要遍历),这就导致我无法遍历栈内元素,所以我还建立了两个同步数组用于遍历。

主要麻烦的是要求(3)考虑算法的健壮性,当表达式错误时,要给出错误原因的提示。这意味着你要将所有的输入错误找出来,就很麻烦,我是找到了以下几种:

bool check(string s)
{
	switch (check_(s))
	{
	case -1:return true;
	case 0: {
		cout << "请按格式输入‘#’!!!" << endl;//#1+1
		break;
	}
	case 1: {
		cout << "表达式禁止为空!!!" << endl;//##
		break;
	}
	case 2: {
		cout << "操作符两端必须有数!!!" << endl;//#*1#,#1*#
		break;
	}
	case 3: {
		cout << "反扣号前没有与之匹配的正括号!!!" << endl;//#)1+1(#
		break;
	}
	case 4: {
		cout << "禁止多个操作符相连!!!" << endl;//#1++1#
		break;
	}
	case 5: {
		cout << "禁止含有非数非运算符的符号!!!(中文括号也不行)" << endl;//###,#ad#
		break;
	}
	case 6: {
		cout << "小数点后禁止接操作符!!!" << endl; //#1.+1#
		break;
	}
	case 7: {
		cout << "一个数内不能有多个小数点!!!" << endl;//#1.1.1#
		break;
	}
	case 8: {
		cout << "括号个数不匹配!!!" << endl;//#(1+1#
		break;
	}
	case 9: {
		cout << "小数点前必须要有数字!!!" << endl;//#.1#
		break;
	}
	case 10: {
		cout << "小数点后必须要有数字!!!" << endl;//#1..1#
		break;
	}
	case 11: {
		cout << "括号内禁止为空!!!" << endl;//#()#
		break;
	}
	}
	system("pause");
	system("cls");
	return false;
}

再就是除数不能为0,因为括号的原因,这个只能在运算过程中判断,所以上面的代码中没有,例子是#5/((2+1)-3)#。值得一提的是,由于计算机对数的存储方式,这里的3-3!= 0,而是等于一个极小的数,所以在判断的时候需要改一下。

if (x <= 8.88178e-15 &&x>= -8.88178e-15) {
			cout << "除数不能为零!!!" << endl;
			result = 8.88178e-15;
		}

其实说上述的判断有重合,也不一定全覆盖,但目前来讲,我是没发现错误,如果真有错,可以告我一声。

[代码实现]

判断表达式是否错误(除除数为0的情况)

int check_(string s)
{
	if (s[0] != '#' || s[s.length() - 1] != '#')
	{
		return 0;
	}
	if (s.length() == 2)
	{
		return 1;
	}
	if (isOperator(s[1]))
	{
		return 2;
	}
	int nq = 0, nh = 0;
	bool point = 0;
	for (int i = 1; i < s.length() - 1; i++)
	{
		if (nh > nq)
		{
			return 3;
		}
		if (isOperator(s[i]))
		{
			if (s[i - 1] == '.')
			{
				return 6;
			}
			if (!isdigit_(s[i + 1]) && s[i + 1] != '(')
			{
				if(s[i+1]=='#')
					return 2;
				return 4;
			}
			point = 0;
		}
		else
		{
			if (s[i] == '(')
				nq++;
			else if (s[i] == ')')
			{
				nh++;
				if (s[i - 1] == '(')
					return 11;
			}
			else if (s[i] == '.')
			{
				if (point == 1)
				{
					return 7;
				}
				if (!isdigit_(s[i - 1]))
				{
					return 9;
				}
				if (!isdigit_(s[i + 1]))
				{
					return 10;
				}
				point = 1;
			}
			else if (!isdigit_(s[i]))
			{
				return 5;
			}
		}
	}
	if (nq != nh)
	{
		return 8;
	}
	return -1;
}
bool check(string s)
{
	switch (check_(s))
	{
	case -1:return true;
	case 0: {
		cout << "请按格式输入‘#’!!!" << endl;//#1+1
		break;
	}
	case 1: {
		cout << "表达式禁止为空!!!" << endl;//##
		break;
	}
	case 2: {
		cout << "操作符两端必须有数!!!" << endl;//#*1#,#1*#
		break;
	}
	case 3: {
		cout << "反扣号前没有与之匹配的正括号!!!" << endl;//#)1+1(#
		break;
	}
	case 4: {
		cout << "禁止多个操作符相连!!!" << endl;//#1++1#
		break;
	}
	case 5: {
		cout << "禁止含有非数非运算符的符号!!!(中文括号也不行)" << endl;//###,#ad#
		break;
	}
	case 6: {
		cout << "小数点后禁止接操作符!!!" << endl; //#1.+1#
		break;
	}
	case 7: {
		cout << "一个数内不能有多个小数点!!!" << endl;//#1.1.1#
		break;
	}
	case 8: {
		cout << "括号个数不匹配!!!" << endl;//#(1+1#
		break;
	}
	case 9: {
		cout << "小数点前必须要有数字!!!" << endl;//#.1#
		break;
	}
	case 10: {
		cout << "小数点后必须要有数字!!!" << endl;//#1..1#
		break;
	}
	case 11: {
		cout << "括号内禁止为空!!!" << endl;//#()#
		break;
	}
	}
	system("pause");
	system("cls");
	return false;
}

其实只要知道有哪些错误,相应的判断代码也很好写,也没什么难点,就稍微麻烦一点。

优先级判断

int getPriority(char ch) 
{
	int level = 0; // 优先级

	switch (ch) {
	case '(':
		level = 1;
		break;
	case '+':
	case '-':
		level = 2;
		break;
	case '*':
	case '/':
		level = 3;
		break;
	default:
		break;
	}
	return level;
}

数字读取

if (isdigit_(ch))
		{ // 如果是数字
			double num = 0;
			double dec = 0;
			int p = 10;//记录小数位数
			int flag = 1;
			do {
				if (ch == '.')
				{
					flag--;
					ch = s[i];
					i++;
					continue;
				}
				if (flag == 1)
				{
					num = num * 10 + (ch - '0');//计算整数位
				}
				else if (flag == 0)
				{
					dec += double((ch - '0')) / p;//计算小数位
					p *= 10;
				}
				ch = s[i];
				i++;
			} while (isdigit_(ch) || ch == '.');

计算

double Calculate(char ch, double x, double y) {
	double result = 0;
	switch (ch) {
	case '+':
		result = x + y;
		break;
	case '-':
		result = y - x;
		break;
	case '*':
		result = y * x;
		break;
	case '/':
		if (x <= 8.88178e-15 &&x>= -8.88178e-15) {
			cout << "除数不能为零!!!" << endl;
			result = 8.88178e-15;
		}
		else
			result = y / x;
		break;
	default:
		break;
	} // switch结束

	return result; // 返回计算得到的结果
}

源代码

#include<iostream>
#include<fstream>
#include<stdlib.h>
#include<stack>
using namespace std;
bool isOperator(char ch) {
	if (ch == '+' || ch == '-' || ch == '*' || ch == '/')
		return true;
	return false; // 否则返回false
}
bool isdigit_(char ch)
{
	if (ch >= '0' && ch <= '9')
		return true;
	return false;
}
int getPriority(char ch) {
	int level = 0; // 优先级

	switch (ch) {
	case '(':
		level = 1;
		break;
	case '+':
	case '-':
		level = 2;
		break;
	case '*':
	case '/':
		level = 3;
		break;
	default:
		break;
	}
	return level;
}
double Calculate(char ch, double x, double y) {
	double result = 0;
	switch (ch) {
	case '+':
		result = x + y;
		break;
	case '-':
		result = y - x;
		break;
	case '*':
		result = y * x;
		break;
	case '/':
		if (x <= 8.88178e-15 &&x>= -8.88178e-15) {
			cout << "除数不能为零!!!" << endl;
			result = 8.88178e-15;
		}
		else
			result = y / x;
		break;
	default:
		break;
	} // switch结束

	return result; // 返回计算得到的结果
}
int check_(string s)
{
	if (s[0] != '#' || s[s.length() - 1] != '#')
	{
		return 0;
	}
	if (s.length() == 2)
	{
		return 1;
	}
	if (isOperator(s[1]))
	{
		return 2;
	}
	int nq = 0, nh = 0;
	bool point = 0;
	for (int i = 1; i < s.length() - 1; i++)
	{
		if (nh > nq)
		{
			return 3;
		}
		if (isOperator(s[i]))
		{
			if (s[i - 1] == '.')
			{
				return 6;
			}
			if (!isdigit_(s[i + 1]) && s[i + 1] != '(')
			{
				if(s[i+1]=='#')
					return 2;
				return 4;
			}
			point = 0;
		}
		else
		{
			if (s[i] == '(')
				nq++;
			else if (s[i] == ')')
			{
				nh++;
				if (s[i - 1] == '(')
					return 11;
			}
			else if (s[i] == '.')
			{
				if (point == 1)
				{
					return 7;
				}
				if (!isdigit_(s[i - 1]))
				{
					return 9;
				}
				if (!isdigit_(s[i + 1]))
				{
					return 10;
				}
				point = 1;
			}
			else if (!isdigit_(s[i]))
			{
				return 5;
			}
		}
	}
	if (nq != nh)
	{
		return 8;
	}
	return -1;
}
bool check(string s)
{
	switch (check_(s))
	{
	case -1:return true;
	case 0: {
		cout << "请按格式输入‘#’!!!" << endl;//#1+1
		break;
	}
	case 1: {
		cout << "表达式禁止为空!!!" << endl;//##
		break;
	}
	case 2: {
		cout << "操作符两端必须有数!!!" << endl;//#*1#,#1*#
		break;
	}
	case 3: {
		cout << "反扣号前没有与之匹配的正括号!!!" << endl;//#)1+1(#
		break;
	}
	case 4: {
		cout << "禁止多个操作符相连!!!" << endl;//#1++1#
		break;
	}
	case 5: {
		cout << "禁止含有非数非运算符的符号!!!(中文括号也不行)" << endl;//###,#ad#
		break;
	}
	case 6: {
		cout << "小数点后禁止接操作符!!!" << endl; //#1.+1#
		break;
	}
	case 7: {
		cout << "一个数内不能有多个小数点!!!" << endl;//#1.1.1#
		break;
	}
	case 8: {
		cout << "括号个数不匹配!!!" << endl;//#(1+1#
		break;
	}
	case 9: {
		cout << "小数点前必须要有数字!!!" << endl;//#.1#
		break;
	}
	case 10: {
		cout << "小数点后必须要有数字!!!" << endl;//#1..1#
		break;
	}
	case 11: {
		cout << "括号内禁止为空!!!" << endl;//#()#
		break;
	}
	}
	system("pause");
	system("cls");
	return false;
}
void run(string s)
{
	stack<double>a;//数栈
	double aa[100] = { 0 };//书栈的同步数组
	int an = 0;
	stack<char>b;//操作符栈
	char bb[100] = { 0 };//操作符栈的同步数组
	int bn = 0;
	int i = 0;
	char ch = s[i];
	i++;
	ch = s[i];
	i++;
	while (1)
	{
		if (ch == '#')
			break;
		if (isdigit_(ch))
		{ // 如果是数字
			double num = 0;
			double dec = 0;
			int p = 10;//记录小数位数
			int flag = 1;
			do {
				if (ch == '.')
				{
					flag--;
					ch = s[i];
					i++;
					continue;
				}
				if (flag == 1)
				{
					num = num * 10 + (ch - '0');//计算整数位
				}
				else if (flag == 0)
				{
					dec += double((ch - '0')) / p;//计算小数位
					p *= 10;
				}
				else
				{
					cout << "数字输入错误!!!" << endl;
					exit(1);
				}
				ch = s[i];
				i++;
			} while (isdigit_(ch) || ch == '.');
			a.push(num + dec); // 存到数栈中
			aa[an] = num + dec;
			an++;
		}
		else if (ch == '(') { // (:左括号
			b.push(ch);
			bb[bn] = ch;
			bn++;
			ch = s[i];
			i++;
		}
		else if (isOperator(ch)) { // 操作符
			if (b.empty()) {// 如果栈空,直接压入栈
				b.push(ch);
				bb[bn] = ch;
				bn++;
				ch = s[i];
				i++;
			}
			else {
				// 比较栈op顶的操作符与ch的优先级
				// 如果ch的优先级高,则直接压入栈
				// 否则,推出栈中的操作符,直到操作符小于ch的优先级,或者遇到(,或者栈已空
				while (!b.empty()) {
					char c = b.top();
					if (getPriority(ch) <= getPriority(c)) {
						// 优先级低或等于
						// 取出栈中操作符和数栈中两个数进行运算,再将结果放回数栈
						double result = 0;
						double x = a.top(); // 第二个操作数,因为栈是后进先出
						a.pop(); an--;
						double y = a.top(); // 第一个操作数
						a.pop(); an--;
						result = Calculate(c, x, y); // 计算
						if (result == 8.88178e-15)
							return;
						a.push(result); // 把计算结果压入栈中
						aa[an] = result;
						an++;
						b.pop(); bn--; // 操作符出栈
					}
					else // ch优先级高于栈中操作符
						break;
				} // while结束
				b.push(ch); // 防止不断的推出操作符,最后空栈了;或者ch优先级高了
				bb[bn] = ch;
				bn++;
				ch = s[i];
				i++;
			} // else
		}
		else if (ch == ')')
		{ // 如果是右括号,一直推出栈中操作符,直到遇到左括号(
			while (b.top() != '(') {
				char c = b.top(); // 取出栈顶操作符
				double result = 0;
				double x = a.top(); // 第二个操作数,因为栈是后进先出
				a.pop(); an--;
				double y = a.top(); // 第一个操作数
				a.pop(); an--;
				result = Calculate(c, x, y); // 计算
				if (result == 8.88178e-15)
				{
					return;
				}
				a.push(result); // 把计算结果压入栈中
				aa[an] = result;
				an++;
				b.pop(); bn--; // 把操作符推出栈
			}
			b.pop(); bn--; // 把左括号(推出栈
			ch = s[i];
			i++;
		}
		if (i == s.length())
			break;
		cout << "当前数栈内容为:";
		for (int i = 0; i < an; i++)
			cout << aa[i] << ' ';
		cout << endl << "当前操作符栈内容为:";
		for (int i = 0; i < bn; i++)
			cout << bb[i] << ' ';
		cout << endl;
	}
	while (!b.empty())
	{
		// 当栈不空,继续取出操作符进行计算
		char c = b.top(); // 取出栈顶操作符
		double result = 0;
		double x = a.top(); // 第二个操作数,因为栈是后进先出
		a.pop(); an--;
		double y = a.top(); // 第一个操作数
		a.pop(); an--;
		result = Calculate(c, x, y); // 计算
		if (result == 8.88178e-15)
			return;
		a.push(result); // 把计算结果压入栈中
		aa[an] = result;
		an++;
		b.pop(); bn--; // 把操作符推出栈
		cout << "当前数栈内容为:";
		for (int i = 0; i < an; i++)
			cout << aa[i] << ' ';
		cout << endl << "当前操作符栈内容为:";
		for (int i = 0; i < bn; i++)
			cout << bb[i] << ' ';
		cout << endl;
	}
	cout << "最终等式为:" << endl;
	for (int i = 1; i < s.length() - 1; i++)
		cout << s[i];
	cout << '=' << a.top() << endl;
	a.pop(); an--;
}
void file_in()
{
	fstream file("data.txt", ios::in);
	if (file.fail())
	{
		cout << "data.txt打开失败!!!" << endl;
		system("pause");
		system("cls");
		return;
	}
	while (!file.eof()) 
	{
		string s;
		file >> s;
		if (file.eof())
			break;
		cout << "文件读入结果为:" << s << endl;
		if (!check(s))
			continue;
		run(s);
		system("pause");
		system("cls");
	}
}
void board_in()
{
	cout << "请输入表达式:(例:#6+15*(21-8/4)#)";
	string s;
	cin >> s;
	if (!check(s))
		return;
	run(s);
	system("pause");
	system("cls");
}
int main()
{
	int choice;
	while (1)
	{
		cout << "1.键入表达式" << endl << "2.文件读入表达式" << endl << "3.退出" << endl;
		cin >> choice;
		if (choice > 3 || choice < 1)
		{
			cout << "请输入正确选项!!!" << endl;
			system("pause");
			system("cls");
			getchar();
			continue;
		}
		break;
	}
	switch (choice)
	{
	case 3:return 0;
	case 1: {
		board_in(); break;
	}
	case 2: {
		file_in(); break;
	}
	}
}

[文件内范例]

#1+1
##
#*1#
#1*#
#)1+1(#
#1++1#
###
#ad#
#1.+1#
#1..1#
#1.1.1#
#(1+1#
#.1#
#5/((2+1)-3)#
#1.#
#()#
#(1+2.65)*5.46/3.2-1.1#

[运行结果]

错误的情况就不展示了,太多了。 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值