计算器程序设计实战
程序功能说明:
用到的头文件和main.cpp函数见上一篇博客:
头文件和main.cpp的传送门
- 正确输入一个“+”,“-”,“*”,“/”,“%”混合运算式会进行运算,并且输出正确的结果
- 输入的运算表达式可以支持带括号“(” “)”
- 输入的运算表达式支持负数运算
- 如果输入的表达式不规范会报相应的错误,比如“primary expected”、“bad token”。
- 如·果每一个运算表达式都正确输入的话,可以继续输入表达式计算。
- 正确输入表达式计算后,如果要退出程序,可以输入“q”退出,输入错误表达式报错误之后,可以输入“hpb”退出。
程序思路说明
实现这个计算器计算的功能,大概需要以下几步:
下面就逐个介绍各个模块:
- Token的实现:
定义Token的目的主要是用于保存运算符的类别以及数值,比如符号“+”,数字20等的保存。根据上述,那么Token可以用一个类来实现:
class Token{
public:
char kind;
double value;
Token(char ch)
:kind(ch), value(0){}
Token(char ch, double val)
:kind(ch), value(val){}
};
在使用Token变量时,只需在程序中把读入的字符存到Token变量中。
- 文法的实现
有了Token可以进行存储用户输入的表达式,但是此时需要解析表达式的含义,比如加减乘除的含义、运算优先级等。所以此时我们就需要定义一个文法,来规定这些含义以及运算规则。
在这个程序中总共有三个函数来处理文法:expression()、term()、primary()。它们实现的伪代码如下:
Expression:
Term
Expression "+" Term
Expression "-" Term
Term:
Primary
Term "*" Primary
Term "/" Primary
Term "%" Primary
Primary:
Number
"("Expression")"
跟据伪代码可以看出来,我们实现的程序中,expression()函数主要处理“+”和“-”号;term()函数主要处理乘、除、取余;primary()函数主要处理括号和数字。
当然我们可以增加计算器计算的范围,比如我们可以计算乘方,这时候只需要在对应文法规则里面加入乘方符号“^”,实现规则即可。
- 单词流的实现
计算器程序的输入是一个单词序列,我们需要从标准输入cin中读入字符,并且能够向程序提供运行时需要的下一个单词。此时用一个“流”比较方便,当我们需要一个单词时,可以调用get()函数从流中产生一个单词,并且可以利用putback()函数把单词放回流中。所以这里我们可以定义一个Token_stream类型。
class Token_stream{
public:
Token_stream();
Token get();
void putback(Token t);
private:
bool full;
Token buffer;
};
4.错误/异常处理
对于错误,有输入的计算式错误、或者运算规则错误(比如0不能做除数等),我们只需要在可能出错的地方抛出错误,然后再进行捕捉程序即可。捕捉错误的主要代码如下:
catch (runtime_error& e){
cerr << e.what() << endl;
keep_window_open("hpb");
return 1;
}
catch (...){
cerr << "exception\n";
keep_window_open();
return 2;
}
5.主函数
主函数调用相关模块,然后打印输出、如有错误捕捉错误即可。代码如下:
int main(){
try{
while (cin){
cout << ">";
Token t = ts.get();
while (t.kind == ';')t = ts.get();//eat ;
if (t.kind == 'q'){
keep_window_open();
return 0;
}
ts.putback(t);
cout << "=" << expression() << endl;
}
keep_window_open();
return 0;
}
catch (runtime_error& e){
cerr << e.what() << endl;
keep_window_open("hpb");
return 1;
}
catch (...){
cerr << "exception\n";
keep_window_open();
return 2;
}
}
- 整个程序的结构
#include “std_lib_facilities.h”
class Token{/*…*/};
class Token_stream{/*…*/};
Token_stream::Token_stream():full(false),buffer(0){/*…*/};
void Token_stream::putback(Token t) {/*…*/};
Token Token_stream::get(){/*…*/};
double primary(){/*…*/};
double term(){/*…*/};
double expression(){/*…*/};
int main(){/*…*/};
运行结果![在这里插入图片描述](https://img-blog.csdnimg.cn/20191023213551101.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwODU5Mzky,size_16,color_FFFFFF,t_70)
总结
计算器程序还可以通过其他方式实现,比如可以用数据结构里面的中缀表达式。