《面向对象程序设计》第四次作业(考虑优先级的表达式计算)

第四次作业分为三个部分:

  • 实现在cmd中调用.exe文件
  • 计算表达式的值(考虑优先级)
  • 在cmd中传参得到运行结果

第一部分get:实现在cmd中调用.exe文件

实现方法:
在main函数的括号中写上int argc, char* argv[]

以前也经常看别人的代码里有这一行东西,现在才知道原来是和命令行有关。其中:

argc记录了用户在运行程序的命令行中输入的参数的个数
argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数是命令行后面跟的用户输入的参数
有些时候程序运行时需要提供一些参数。比如copy命令,需要指明源文件和目标文件名,就得通过argc和argv来传递

在主函数里修改了input参数为argv[argc-1],调用strcmp函数比较argv[1]-a来实现同行输入不同参数个数的不同处理效果。
(这步折腾了好久,发现好多有趣的bug. 比如在编译运行的时候没有输入任何参数输出了一排.exe的文件位置..控制台表示不管用了,只能在cmd里手动键入调试。每每手滑关了又得一步步cd找文件夹 gg)

本部分参考资料:
int main(int argc,char* argv[])详解


第二部分get:计算表达式的值(考虑优先级)

(代码已上传github: Calculator_ver2.0

在本部分中,考虑了计算的优先级和负数的情况。其中优先级以小括号内运算,乘除运算,加减运算为先后顺序。由于负数的位置只可能出现在字符串首或接在左括号之后,所以在操作的时候没有预处理负数的情况,只是在负号前多加了一位0,即将计算 (-1+2)转化为计算 (0-1+2)。为了确保运算结果的正确性,在这里把减法的优先级提到加法之前。
由于最后只输出栈顶元素,所以栈底多加的那个0无论首个数字为正为负都不会有影响。
以及第一次使用字符串流,真是个好东西_(:зゝ∠)_

实现方法:
step1:将scan类中扫描后的中缀表达式队列转化为后缀表达式后,存入suff队列中。
ps:在测试了多组数据之后发现还是得特判两种负号的情况,前一次代码会出现 -2*2+1*(-2+3)= -5的情况。这是因为-号的优先级低于*,但考虑到正常的减法不能再简单的调整优先级。所以在trans方法中添加了特判了两种位置负号的语句。[04.07补充]
ps:补充了负号出现的第三种位置。类似于:-(-()),所以之前压入空栈的一个0改为有几个负号压入几个0。调泡泡那个很长串的表达式调了很久,又发现了不少bug,由于没有预处理负数所以用了朴素的各种特判解决了(。[04.09补充]

主要代码:

void Calculation::trans(queue<string> str)
{
	string temp = "";
	temp = str.front();

	if(temp == "-")
	{
		minus++;

		str.pop();
		//特判第一个负数
		if(isdigit(str.front()[0]))
		{
			suff.push(str.front());
			str.pop();
			suff.push(temp);
		}
		//-()的情况
		else
		{
			//标记为-<)
			str.front() = "<";
			oper.push(temp);
		}
	}

	//遍历中缀表达式队列
	bool flag = true;
	bool isminus = true;

	while(!str.empty())
	{
		flag = true;
		//读取队列第一个元素
		temp = str.front();
		//若是(,直接入栈
		if(temp == "(" || temp == "<")
		{
			oper.push(temp);
			str.pop();
			if(isdigit(str.front()[0]))
			{
				isminus = false; //不是负数
			}
			flag = false;
		}
		//若是*或/,直接入栈
		if(temp == "*"||temp == "/")
		{
			oper.push(temp);
		}
		//若是+,判断栈顶元素,若低于栈顶元素优先级,则栈顶元素出栈后该元素入栈;否则直接入栈
		if(temp == "+")
		{
			//考虑到负数情况,所以先做减法
			while(!oper.empty()&&(oper.top() == "*"||oper.top() == "/"||oper.top() == "-"))
			{
				suff.push(oper.top());  //进入后缀运算表达式队列
				oper.pop();  //弹出栈顶元素
			}
			oper.push(temp);  //当前元素入栈

		}
		//若是-
		if(temp == "-")
		{
			minus++;

			//括号内有负号的情况
			if(!oper.empty() && oper.top() == "(")
			{
				//负数
				if(isminus)
				{
					str.pop(); //弹出-号
					suff.push("0"); //0入队列
					suff.push(str.front()); //此负数数值入队列
					suff.push(temp); //-号入队列
				}
				//-()
				else
				{
					oper.push(temp);
				}
			}
			//该-号为减法符号
			else
			{
				//若-低于栈顶元素优先级,则栈顶元素出栈后该元素入栈;否则直接入栈
				while(!oper.empty()&&(oper.top() == "*"||oper.top() == "/"))
				{
					suff.push(oper.top());  //进入后缀运算表达式队列
					oper.pop();  //弹出栈顶元素
				}
				oper.push(temp);  //当前元素入栈

			}
		}
		//若是),则依次弹出栈顶元素直到遇到(
		if(temp == ")")
		{

			while(oper.top()!="(" && oper.top()!="<")
			{
				suff.push(oper.top());
				oper.pop();
			}

			if(oper.top() == "<")
			{
				oper.pop();//弹出<
				suff.push(oper.top());//-号入队列
			}

			oper.pop();

			isminus = true;

		}
		//若是运算数字,则入队列
		else if(isdigit(temp[0]))
		{
			suff.push(temp);
		}

		if(flag)
		{
			str.pop();
		}
	}
	while(!oper.empty())
	{
		suff.push(oper.top());
		oper.pop();
	}
}

step2:扫描后缀表达式队列,若为待计算数字的字符串则用sstream转化为数字后,压入存数字的栈num中;若为计算操作符则弹出num栈顶元素进行计算后再入栈。最终num的栈顶即为运算结果。

主要代码:

int Calculation::calcu()
{
	num.push(resu); //将0压入栈,用以处理负数
	stringstream ss;
	int tempnum;

	while(!suff.empty())
	{
		string temp = suff.front();
		//对栈顶元素进行对应操作运算,运算结果入栈
		if(temp == "+")
		{
			resu = num.top();
			num.pop();
			resu += num.top();
			num.pop();
			num.push(resu);
		}
		if(temp == "-")
		{
			resu = num.top();
			num.pop();
			resu = num.top() - resu;
			num.pop();
			num.push(resu);
		}
		if(temp == "*")
		{
			resu = num.top();
			num.pop();
			resu *= num.top();
			num.pop();
			num.push(resu);
		}
		if(temp == "/")
		{
			resu = num.top();
			num.pop();
			resu = num.top() / resu;
			num.pop();
			num.push(resu);
		}
		//若为数字字符串,则转化为数字后入栈
		else if(isdigit(temp[0]))
		{
			ss << suff.front();
			ss >> tempnum;
			num.push(tempnum);
			ss.clear();
		}
		suff.pop();
	}
	return num.top();
}

本部分参考资料:
前缀、中缀、后缀表达式
c++ 字符串流 sstream(常用于格式转换)


第三部分get:在cmd中传参得到运行结果

愉快地按要求输出了结果。

听说不能正确计算出第一反人类表达式的计算器不是好游戏机

(来自橘子犇犇博客下评论区的梗)

posted @ 2016-04-03 11:21  thousfeet  阅读(567)  评论(12编辑  收藏  举报
/* */