《面向对象程序设计》第四次作业(考虑优先级的表达式计算)
第四次作业分为三个部分:
- 实现在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中传参得到运行结果
愉快地按要求输出了结果。
听说不能正确计算出第一反人类表达式的计算器不是好游戏机