计算器第四次作业——实现
自独善上次作业后,余继而观此作业,无力不从心之感,虽有坎坷,究竟有方可循,有法可依,并不似上次那般,毫无头绪,乱来一气,此乃进步也,心中百般惊喜,自不再话下。几经修改,终是成功,虽难登大雅之堂,亦有可圈可点之处。欲知代码为何,轻触此处。
以余之愚见,此次作业,重点如下:知如何调用,明有无参数,能及时报错,懂数符转换,辨负号减号,晓孰先孰后,会处理括号。其中,又以后三者为难点。具体为何,且听余细细道来。
一、知如何调用
调用之法甚易,先添int argc, char* argv[]
于int main()
括号中,即为int main(int argc, char* argv[])
。此题要求用命令行进行运行,故而不用输入语句。而后按window+r
,则现一搜索窗口,输cmd
并按确认,便可见一黑底白字窗口,输入.exe
文件之路径,并输表达式,按回车即可得结果。其中,argc
是命令行总的参数个数,每输入一个参数,它会自动加一。argv[]
则是argc
个参数的数组,其中第0个参数是目标程序的全名,即路径加可执行文件名,以后的参数命令行后面跟的是用户输入的参数。换言之,在main
函数里的cin>>string
被string=argv[]
替代,即string
由qrgv[]
传入值,而不是cin
。比如,此题中,应先输入路径(假设为e\calculator.exe
),再输入表达式(假设为6+3-7*8
),即,argv[0]="e\calculator.exe"
,argv[1]="6+3-7*8"
,那么我们需要的就是argv[1]
.此外,须知没有cin
语句,无法用编译器直接运行,只能通过此法。
二、明有无参数
依题之要求,如见参数"-a",需先输出原表达式,再输出结果。余之做法如下:判断输入的参数个数,如果是3个,说明包含"-a",并做一标记。最后,如有标记,则先输原表达式,加"="再输结果。
if (argc == 3)
{
k = 1; //标记是否有参数-a
string = argv[2]; //由命令行传入string的值
}
if (argc == 2)
string=argv[1];
......
if (k == 1) //有参数“-a”,输出原表达式
print.printstring(q);
cout << output << endl; //输出结果
示例:
三、能及时报错
报错应分为三类:其一,计算过程中数字长度超过10位;其二,除数为零;其三,括号不匹配。具体如下:
其一
int length_check(double number) //检查计算过程中数字是不是超出范围
{
int n;
string string;
stringstream stream;
stream.clear(); //
stream << number; //数字转字符串
stream >> string; //
n = string.size();
if (n > 10)
return 0;
else
return 1;
}
......
if (length_check(t) == 0) //数字长度的判断
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
其二
if (g == "/"&&f == 0) //g为运算符号,f为除数
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
其三
扫描整个表达式,遇"(",则将其压入栈,遇")",则删栈顶元素,最后如栈为空,则正确匹配。
int parentheses_check(string str) //判断括号是否匹配
{
stack<string>s;
int i, length;
length = str.size();
for (i = 0; i < length; i++)
{
if (str[i] == '(')
s.push("(");
if (str[i] == ')')
{
if (!s.empty() && s.top() == "(")
s.pop();
else
{
return 0;
break;
}
}
}
if (s.empty())
return 1;
else
return 0;
}
......
if (parentheses_check(string) == 0)
cout << "ERROR:parentheses are not matched!" << endl;
}
四、懂数符转换
数字转与字符串之间互相转换,可添加#include<sstream>
用stringstream
实现。注意,如多次转换,需对stream
进行清除。
如对double a
和string b
数字转字符串
#include<sstream>
......
stream.clear();
stream << a;
stream >> b;
字符串转数字
#include<sstream>
......
stream.clear();
stream << b;
stream >> a;
五、辨负号减号
显然,在一正确表达式中,“-”为负号只有两种情况,一是位于首位,二是处于“(”之后。在此题中,扫描时直接把负号归为数字的一部分,一同存进队列。而减号则作为运算符直接入队,这样就把负号和减号分开了。
for (i = 0; i < input.size(); i++)
{
if ((input[i] >= '0' && input[i] <= '9') //对数字和“-”的处理
|| input[i] == '.' || input[i] == '-')
{
if (input[i] == '-')
{
if ((i - 1 >= 0 && input[i - 1] == '(') //负号的判断
|| (i == 0))
temp = "-";
else
{
queue.push(temp);
temp.clear();
queue.push("-");
}
}
else
temp += input[i];
}
else
{
if (!temp.empty())
queue.push(temp);
temp.clear();
temp = input[i];
if (!temp.empty())
queue.push(temp);
temp.clear();
}
}
if (!temp.empty())
queue.push(temp);
return queue;
}
六、晓孰先孰后
计算,先后顺序尤为重要,故必先设法定顺序,方可正确计算。此处暂且不论括号,余先以数字规定运算符之大小,
int priority(string c) //判断优先级
{ //此处只对针加、减、乘、除
if (c == "+" || c == "-") //四种运算,括号下面另外处理
return 0;
if (c == "*" || c == "/")
return 1;
}
并借用2栈之进出,进行计算。二栈为何?一曰数栈,专存数字,一曰符栈,用以放运算符。访队列首元并删之,如为运算符,压入符栈,如为数字字符串,转为数字(上以具述)后,压入数栈。压入符栈前,需先比其与栈顶元素之大小。唯符栈为空或其值大于栈顶元素,方可将其压入;如若不然,取数栈顶两元素,先行计算,得其值而压入数栈。计算以一函数为之,
double calculate1(double a, string c, double b) //用于计算的函数
{
if (c == "+")
return (a + b);
if (c == "-")
return (a - b);
if (c == "*")
return (a*b);
if (c == "/")
return (a / b);
}
如此反复,直至队列为空。切记,访问队列元素之后,要将其删除,否则或是无法输出,或是结果出错,贻害无穷,余深受其害,特告知诸君。此处不贴代码,下面一起贴出。
七、会处理括号
括号,运算之君王也,万事以其为先。然此子狡诈异常,余甚为头疼,只得以乡村土法治之。遇括号,则取其中元素于另一栈中,再进行计算。如此叙述较为抽象,以下将连上面代码一起给出,内容略长,望诸君耐心详读,其中良莠,诸君自判,如有妙法,请传授于余,余感激不尽。
while (!que.empty()) //队列不为空则进入
{
if (que.front() == "(") //对括号内的部分进行处理(开始)
{ //遇到左括号,压入
s_str.push("(");
que.pop();
}
else if (que.front() == ")") //遇到右括号
{
que.pop();
while (s_str.top() != "(") //把字符栈里的符号弹出,压入一个队列
{
q_temp.push(s_str.top());
s_str.pop();
}
s_str.pop();
while (!q_temp.empty()) //进行计算
{
f = s_num.top();
s_num.pop();
e = s_num.top();
s_num.pop();
g = q_temp.front();
q_temp.pop();
if (g == "/"&&f == 0) //除数是否为零的判断
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
t = calculate1(e, g, f); //进行计算
if (length_check(t) == 0) //数字长度的判断
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
s_num.push(t); //把结果压入数字栈
}
} //(结束)
else if (que.front() == "+" || que.front() == "-" //对括号外部分的计算
|| que.front() == "*" || que.front() == "/")
{
if (s_str.empty() || s_str.top() == "(")
{
s_str.push(que.front());
que.pop();
}
else
{
if (priority(que.front()) > priority(s_str.top()))
{
s_str.push(que.front());
que.pop();
}
else
{
g = s_str.top();
s_str.pop();
f = s_num.top();
s_num.pop();
e = s_num.top();
s_num.pop();
if (g == "/"&&f == 0)
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
t = calculate1(e, g, f);
if (length_check(t) == 0)
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
s_num.push(t);
}
}
}
else
{
stream.clear();
stream << que.front();
stream >> change;
s_num.push(change);
que.pop();
}
}
while (!s_str.empty())
{
f = s_num.top();
s_num.pop();
e = s_num.top();
s_num.pop();
g = s_str.top();
s_str.pop();
if (g == "/"&&f == 0)
{
cout << "ERROR:divisor can't be zero!" << endl;
break;
}
t = calculate1(e, g, f);
if (length_check(t) == 0)
{
cout << "ERROR:digital length is beyong clculation!";
break;
}
s_num.push(t);
}
final_result = s_num.top();
return final_result;
}
以上内容和代码为本菜鸟之愚知拙见,如有不当和错误之处,纯属正常,望诸君开慧眼识不足,并启金口传于余,余定当感激不尽。