1 总观全局
我们需要两个栈,一个操作数栈(整数栈),一个操作符栈(字符栈)。
2 分部进行
我们利用getchar()函数接受键盘输入来的字符:
* 若是数字:
我们把它存入一个字符串中,继续接收字符并判断它是否是数字,若还是数字我们继续往字符串里存,否则我们将该字符串转换为整数(我们利用atoi()函数)。然后存入操作数栈中。大概代码实现:
- if (isnumber(elem))
- {
- string str;
- NUM:str += elem;
- elem = getchar();
- if (isnumber(elem))
- {
- goto NUM;
- }
- else
- {
- int inum = atoi(str.c_str());
- _stack_operand.push(inum);
- }
- }
其中isnumber()函数是我自定义的来判断所接收的字符是否是数字的函数。它的实现是这样的:
- bool expression_calculator::isnumber(const char elem) //判断是否是数字
- {
- if (elem >= '0' && elem <= '9')
- {
- return true;
- }
- return false;
- }
对于我上面第一个程序中用到了万人唾弃的goto语句,但我觉得它用到这里很好。很方便,否则又会多添几行代码,故此在我后面的程序中也用到了goto语句。
*若是操作数或是括号
我们首先判断一下操作符栈是否为空:
若为空:
操作符入栈,继续得到字符。
若不为空:
我们需对我们所得到的字符进行进一步的判断,在这里我用了一个switch语句:
case '+':
如果我们得到的字符是'+':
由于我们是从左往右计算的,故可以说'+'的优先级是最低的了,没有比他更低了。我们这里需要判断一下操作符栈的栈顶操作符和当前操作符的优先级了:
若操作符栈为空:
当前操作符入栈。
若操作符栈不为空:
在这种情况之下只有'('比'+'的优先级底,其他任何操作符都会比'+'的优先级高:
故:
case '(':
当前操作符入栈;
继续得到字符;
break;
default:
操作数的栈顶出栈给右操作数;
操作的栈顶出栈给左操作数;
操作符栈顶出栈;
根据栈顶操作符来计算值,并将结果入栈(操作数栈);
判断此刻栈是否为空;
若不为空:跳到 本次判断的开始,继续判断栈顶元素的类型。
若栈为空:将当前操作符入栈,并继续得到字符;
break;
对于case '+':的实现大概是这样的:
- case '+':
- ADD:switch (_stack_operator.top())
- {
- case '(': //栈顶元素的优先级底
- _stack_operator.push(elem);
- elem = getchar();
- break;
- default: //栈顶元素的优先级高
- int lhs,rhs;
- rhs = _stack_operand.top();
- _stack_operand.pop();
- lhs = _stack_operand.top();
- _stack_operand.pop();
- _stack_operand.push(Operate(lhs,_stack_operator.top(),rhs));
- _stack_operator.pop();
- if (!_stack_operator.empty())
- {
- goto ADD;
- }
- else
- {
- _stack_operator.push(elem);
- elem = getchar();
- }
- break;
- }
- break;
这里同样用到了goto语句,想又该被骂了。。呵呵。对于其中的Operate()函数介绍的最后了。是在不好意思。
对于其他的 '-','*','/','%'操作符基本相同,仅是优先级不同的差别。
如:
case '*':
这时候除了'+', '-' , '('的优先级比'*'低以外,其他的操作符均比'*'的优先级高.
break;
其他的类似情况不在赘述。
得到的字符除了数字和操作符外,我们还可能得到'(' , ')'、故我们仍需对这两个字符进行判断.
由于'('操作符的优先级高于其他任何操作赋,故在这里只需做一件事(当前操作符入栈),然后继续得到字符就OK了.代码实现是这样的:
- case '(':
- //此时'('的优先级最高,无论栈顶是什么
- _stack_operator.push(elem);
- elem = getchar();
- break;
对于另外一种操作符')'的情况就稍微较之麻烦一点了:
case ')':
由于此时除了'('操作符外,其他任何操作符的优先级都比')'高。故我们在这里这样处理:
如果是 '(': 操作符栈顶出栈(即'('出栈).并继续得到字符,此时我们也冲掉了')'操作符,因为此时我们无需对它进行处理。
如果是其他的操作符;与上面一样,操作数出栈,操作符出栈,然后计算结果入栈。
..........
break;
这里的代码实现大概是这样的:
- case ')':
- //此时'('的优先级最低,除了栈顶是'('
- RBREAKET:switch (_stack_operator.top())
- {
- case '(':
- _stack_operator.pop();
- elem = getchar();
- break;
- default:
- int lhs,rhs;
- rhs = _stack_operand.top();
- _stack_operand.pop();
- lhs = _stack_operand.top();
- _stack_operand.pop();
- _stack_operand.push(Operate(lhs,_stack_operator.top(),rhs));
- _stack_operator.pop();
- if (!_stack_operator.empty())
- {
- goto RBREAKET;
- }
- else
- {
- _stack_operator.push(elem);
- elem = getchar();
- }
- break;
- }
- break;
这样同样用到了goto语句。到此基本上我们的处理已完毕,我们看下处理的结果.
经过我们上述处理后:
操作符栈中仅剩下一个操作符,操作数栈中仅剩下最后一个操作符。我们将之处理后便是我们最后的结果;我的代码大概是这样的:
- int lhs,rhs;
- rhs = _stack_operand.top();
- _stack_operand.pop();
- lhs = _stack_operand.top();
- _stack_operand.pop();
- _stack_operand.push(Operate(lhs,_stack_operator.top(),rhs));
其中,Operate()函数是我自定义的进行计算的函数,它的参数是两个操作数和当前栈顶的操作符。它的实现是这样的:
- int expression_calculator::Operate(int ilhs,const char Operator,int irhs)
- {
- switch (Operator)
- {
- case '+':
- ilhs += irhs;
- break;
- case '-':
- ilhs -= irhs;
- break;
- case '*':
- ilhs *= irhs;
- break;
- case '/':
- ilhs /= irhs;
- break;
- case '%':
- ilhs %= irhs;
- break;
- }
- return ilhs;
- }
现在ilhs的值既是我们的结果。
到此整个表达式求值过程完成.
在此说明一下程序的使用:
程序运行后的效果如图:
在此我们只需输入运算表达式即可,但别忘了以'#'结尾。这里的表达式的数字仅考虑的整数,并没有考虑浮点数在内。
其中字符可以为 '0'~'9', '+' , '-' , '*' , '/' , '%', '(' , ')'.
其中'(',')'可以嵌套,即可以有多个镶嵌的括号。
令:本程序并没有对错误的表达式进行处理。即若是输入错误的表达式,如:括号不匹配,连着两个运算符等。这个程序是无法判别的,此时的结果将是一个错误的结果。
若那位朋友想对此进行处理的话,思路可以是这样的:
① 处理连着两个运算符的情况
有一个字符变量来存储上一个字符。每次得到新字符时。即对当前字符和上一个字符进行判断。若他们是这样的组合时:
操作符 , 操作符
操作符 ,括号
括号,操作符
即可认为此表达式错误。无效。
② 处理括号匹配的情况
我们可以单独创建一个栈来存储括号。得到括号后,即判断当前的括号和栈顶的括号是否配对,若配对,栈顶出栈,继续得到字符。否则入栈。最后判断栈是否为空,若为空即为配对。否则:即可认为括号不匹配。认为当前的表达式错误,无效。
对于上面两种情况我们可以在一个单独的函数里进行处理。
对于其他出错情况均可在该函数函数进行处理。