200多行代码写一个能实现加减乘除的计算器
中缀表达式和后缀表达式:
1 + 3 * 5
这样的表达式是中缀表达式,是一种日常的表达方法,能够便于理解其运算顺序
- 其优点是整洁,字面意思便于理解
- 缺点是需要考虑运算符的优先级
1 3 5 * +
这样的表达式是后缀表达式,他可以从中缀表达式转换过来,对于计算机,后缀表达式更方便计算,并且不需要考虑运算符优先级
中缀表达式转换为后缀表达式
中缀表达式转换为后缀表达式的方法为,以一个符号栈来存储符号,一个数组来存储后缀表达式的信息,从前往后扫描表达式的信息,符号信息可能为如下几种情况:
数字
、括号
、运算符
并且对于每一种符号都有不同的处理
- 如果符号为数字,则将其按顺序放入后缀表达式队列中
- 如果符号为左括号
(
,将其入栈- 如果符号为右括号
)
,一直出栈,将元素按顺序放入后缀表达式队列中,知道遇到左括号- 如果为运算符
s1
,则与栈顶运算符(s2
)比较优先级
- 若运算符优先级s1 > s2,则直接入栈
- 否则则,将栈顶元素放入后缀表达式队列,继续与新的栈顶元素比较,直到栈空或者运算符优先级大于栈顶运算符
实现前准备:
需要一个栈
#define STACK_SIZE 200
template<typename T>
struct Stack{
T *elem;
int top; // 栈顶指针
T empty; // 当栈空时返回的元素
Stack(){
elem = (T*)calloc(STACK_SIZE, sizeof(T));
top = -1;
memset(&empty, 0, sizeof(T));
}
~Stack(){
free(elem);
}
inline bool Empty(){
return top == -1;
}
};
// 入栈操作
template<typename T>
void push(Stack<T> &stack, T elem){
if(stack.top == STACK_SIZE)
return;
stack.top++;
stack.elem[stack.top] = elem;
}
// 出栈操作
template<typename T>
T pop(Stack<T> &stack){
if(stack.Empty())
return stack.empty;
T t = stack.elem[stack.top];
stack.top--;
return t;
}
需要一个TOKEN类型,因为字符串无法直接进行运算, 后缀表达式数组元素就是以TOKEN类型组织的
enum TOKEN{INT, // 整数
ADD, // 加法
SUB, // 减法
MUL, // 乘法
DIV, // 除法
NODEF // 未定义};
struct Token{
TOKEN token_id; // 符号类型
int val; // 属性值
Token(TOKEN token, int val):token_id(token),val(val){}
Token(): Token(NODEF, 0){}
};
其它需求
// 一些宏定义
#define STACK_SIZE 200 // 栈的最大空间
#define TOKEN_SIZE 200 // 后缀表达式元素最大长度
#define ISDIG(s) (s - '0' >= 0 && s - '0' <= 9) // 判断字符是不是数字
// 运算符字符集合
const std::set<char> OperatorSet = {'+', '-', '*', '/'};
// 根据符号获得运算符的类型
TOKEN OperatorStyle(const char s){
if(s == '+')
return ADD;
if(s == '-')
return SUB;
if(s == '*')
return MUL;
if(s == '/')
return DIV;
return NODEF; // 未定义
}
// 处理运算符符号,将其转化为对应的TOKEN
// 对于运算符,如果运算符大于栈顶运算符,则将其压栈
// 如果小于,则将运算符出栈,再进行下一轮的比较,知道大于栈顶运算符或栈空
bool DealOperator(Stack<char> &stack, char s, int &j){
// 确保s是一个运算符
// isoper 判断是否为一个运算符
bool isoper = OperatorSet.find(s) != OperatorSet.end();
if((stack.Empty() && isoper) || (stack.elem[stack.top] == '(')){
push(stack, s);
return true;
}else if(isoper){
if(OperatorPrec(s, stack.elem[stack.top])){
push(stack, s);
return true;
}else{
Token t;
t.token_id = OperatorStyle(pop(stack));
tokens[j] = t;
j++;
return DealOperator(stack, s, j);
}
}else {
return false;
}
}
将一个中缀表达式转换为可计算的Token类,并按顺序放入后缀表达式列表中
// 将前缀表达式转换为后缀表达式
int precToLast(const char* expr){
Stack<char> stack;
int i =0, j = 0;
int len = strlen(expr);
char s;
for(;i < len;){
s = expr[i];
if(ISDIG(s)){
Token t;
char p[] = {};
int k = 0;
while(i < len){
p[k] = s;k++;i++;
if(i >= len)
break;
s = expr[i];
if(s == '(' || s == ')' || OperatorSet.find(s) != OperatorSet.end())
break;
else if(!ISDIG(s)){
std::cerr<<"Character error: "<<s<<std::endl;
abort();
}
}
int z = 0;
while(k > 0){
t.val += (p[z] - '0')*std::pow(10, k-1);
k--;z++;
}
t.token_id = INT;
tokens[j] = t;
j++;
continue;
}else{
if(s == '('){
push(stack, s);
}else if(s == ')'){
char s1;
while(!stack.Empty()){
Token t;
s1 = pop(stack);
if(s1 == '(')
break;
t.token_id = OperatorStyle(s1);
tokens[j] = t;
j++;
}
}else{
if(!DealOperator(stack, s, j)){
std::cerr<<"Error Character"<<std::endl;
abort();
}
}
}
i++;
}
// 最后把栈里面的符号压出来
while(!stack.Empty()){
s = pop(stack);
if(OperatorSet.find(s) != OperatorSet.end()){
Token t;
t.token_id = OperatorStyle(s);
tokens[j] = t;
j++;
continue;
}else if(s == '(')
continue;
}
return j;
}
后缀表达式的计算
- 遇到整数类型,则将其压入栈中
- 遇到运算符,则出栈两个整数,进行运算后得到的结果压回栈中
// 计算后缀表达式得到结果
int calculateExpr(int len){
int scan = 0;
Stack<Token> stack;
for(; scan < len; ++scan){
if(tokens[scan].token_id == INT){
push(stack, tokens[scan]);
}else{
if(tokens[scan].token_id == ADD){ // 加法
if(stack.Empty()){
std::cerr<<"expr error"<<std::endl;
abort();
}else{
Token t = pop(stack);
if(!stack.Empty()){
t.val += pop(stack).val;
}
push(stack, t);
}
}
else if(tokens[scan].token_id == SUB){ // 减法
if(stack.Empty()){
std::cerr<<"expr error"<<std::endl;
abort();
}else{
Token t = pop(stack);
if(!stack.Empty()){
stack.elem[stack.top].val += t.val;
}else{
t.val = -t.val;
push(stack, t);
}
}
}else if(tokens[scan].token_id == MUL){ // 乘法
if(stack.Empty()){
std::cerr<<"expr error"<<std::endl;
abort();
}else{
Token t = pop(stack);
if(!stack.Empty()){
stack.elem[stack.top].val *= t.val;
}else{
std::cerr<<"expr error"<<std::endl;
abort();
}
}
}else if(tokens[scan].token_id == DIV) { // 除法
if(stack.Empty()){
std::cerr<<"expr error"<<std::endl;
abort();
}else{
Token t = pop(stack);
if(!stack.Empty()){
stack.elem[stack.top].val /= t.val;
}else{
std::cerr<<"expr error"<<std::endl;
abort();
}
}
}
}
}
return stack.elem[stack.top].val;
}
测试
// main 函数
int main() {
char* argv[] = {"+100","-100+40","100+200", "1+30*5", "5*(1+3)", "1+4/2", "(1+4)/2","3-8/4","1000*(1+2)"};
int argc = 9;
int i;
for(i=0; i<argc; ++i){
printf("Expr: [%s = %d]\n", argv[i],calculateExpr(precToLast(argv[i])));
}
return 0;
}
结果
Expr: [+100 = 100]
Expr: [-100+40 = -60]
Expr: [100+200 = 300]
Expr: [1+30*5 = 151]
Expr: [5*(1+3) = 20]
Expr: [1+4/2 = 3]
Expr: [(1+4)/2 = 2]
Expr: [3-8/4 = 5]
Expr: [1000*(1+2) = 3000]