实现要求:
(1) 以字符序列的形式从终端输入语法正确的、不含变量的整数表达式。利用 下表给出的算符优先关系,实现对算术混合运算表达式的求值,并仿照求值中运 算符栈、运算数栈、输入字符和主要操作的变化过程。
(2) 扩充运算符集,如增加乘方、单目加减等运算。
思路:
借助一个存放运算数的栈和一个存放运算符的栈来实现计算器的计算和检错功能
头文件代码如下:
#ifndef EXPR_H
#define EXPR_H
#include <string>
#include <stack>
using namespace std;
bool bracketError(string& p); //判断是否有括号匹配错误
bool isOper(char& p); //判断是否是运算符
void Calculate(stack<double>& nums,char& p, int& oneortwo); //进行计算,变量oneortwo判断是单目运算还是双目运算
bool isInt(double& n); //判断一个数是否是整数
bool isEven(double& n); //判断一个数是否是偶数
void Print_num(stack<double>& p); //输出运算数栈
void Print_ope(stack<char>& p); //输出运算符栈
void Final(string& exp); //计算最终结果
#endif
上述函数的定义的代码如下:
#include "Expr.h"
#include<ctype.h>
#include<stack>
#include<iostream>
#include<string>
#include<cstdio>
#include <unordered_map>
#include <cmath>
using namespace std;
stack<double> nums; //运算数栈
stack<char> opes; //运算符栈
unordered_map<char, int> Priority{ {'#',0} ,{'+',1},{'-',1},{'*',2},{'/',2},{'%',2},{'^',3}}; //运算符优先级
bool bracketError(string& p) {
stack<char> t;
for (int i = 0; i < p.length(); i++) {
if (p[i] == '(') {
t.push(p[i]); //左括号入栈;
}
if (p[i] == ')' && t.top() == '(') {
t.pop(); //栈顶为左括号且p[i]为右括号时出栈
}
}
if (t.empty()) { //栈为空说明匹配成功,否则说明有括号匹配错误
return 0;
}
return 1;
}
bool isOper(char& p) {
if (p == '+' || p == '-' || p == '*' || p == '/' || p == '^' || p == '%') {
return 1;
}
return 0;
}
bool isInt(double& n) {
if (n - (int)n == 0) {
return 1;
}
return 0;
}
bool isEven(double& n) {
if (isInt(n)) {
if ((int)n % 2 == 0) {
return 1;
}
else {
return 0;
}
}
return 0;
}
void Calculate(stack<double>& nums, char& p, int& oneortwo ) {
if (nums.empty()) { return; }
if (oneortwo == 1) //做单目运算
{
if (p == '-') {
printf("进行单目减运算\n");
nums.top() *= -1;
}
else {
printf("进行单目加运算\n");
}
Print_num(nums);
Print_ope(opes);
oneortwo = 2;
}
else {
double b = nums.top(); nums.pop();
double a = nums.top(); nums.pop();
double ans = 0;
double tmp = 0;
if (b != 0) {
tmp = 1 / b;
}
switch (p) {
case'+': {printf("进行加法运算:%f + %f\n",a,b); ans = a + b; } break;
case'-': {printf("进行减法运算:%f - %f\n", a, b); ans = a - b; }break;
case'*': {printf("进行乘法运算:%f * %f\n", a, b); ans = a * b; }break;
case'/': {
printf("进行除法运算:%f / %f\n", a, b);
ans = a / b;
}break;
case'^': {
printf("进行乘方运算:%f ^ %f\n", a, b);
ans = pow(a, b);
}break;
case'%': {
ans = (double)((int)a % (int)b);
cout << "进行取模运算:" << a << " % " << b << endl;
}break;
}
nums.push(ans);
printf("主要操作:nums.push(%f),push的元素为%f\n", ans, ans);
Print_num(nums);
}
}
void Print_num(stack<double>& p) {
stack<double> tmp;
while (!p.empty()) {
tmp.push(p.top()); p.pop();
}
printf("当前运算数栈中元素如下:\n" );
while (!tmp.empty()) {
p.push(tmp.top()); printf("%f ",tmp.top());
tmp.pop();
}
cout << endl;
}
void Print_ope(stack<char>& p) {
stack<char> tmp;
while (!p.empty()) {
tmp.push(p.top()); p.pop();
}
printf("当前运算符栈中元素如下:\n");
while (!tmp.empty()) {
p.push(tmp.top()); printf("%c ", tmp.top());
tmp.pop();
}
cout << endl;
}
void Final(string& exp) {
while (!nums.empty()) { nums.pop(); }
while (!opes.empty()) { opes.pop(); } //确保运算数栈nums和运算符栈opes为空栈才能进行后续的操作
opes.push('#'); //防止单目运算判断有误,先压入一个‘#’字符
int oneortwo = 2; //用来判断是单目运算还是双目运算
if (bracketError(exp)) { printf("Error:有括号匹配错误\n"); return; }
for (int i = 0; i < exp.size()&&(opes.top()!='#'||exp[i]!='#');i++) {
if (exp[i] == '#' && i != exp.size() - 1) { printf("#只能放在表达式的末尾\n"); return; }
//单目运算的情况:+或-是表达式的第一个元素;+或-在左括号后面。
if (i == 0 && (exp[i] == '+' || exp[i] == '-') && isdigit(exp[i + 1])) { oneortwo = 1; }
if (i >= 1 && (exp[i] == '-' || exp[i] == '+') && exp[i - 1] == '(') { oneortwo = 1; }
//+、-、*、/、^、%、( 后面若直接跟着+、-、*、/、^、%、),说明有无效符,输入有误
if ((exp[i] == '+' || exp[i] == '-' || exp[i] == '*' || exp[i] == '/' || exp[i] == '^' || exp[i] == '%'||exp[i]=='^') && (exp[i + 1] == '+' || exp[i + 1] == '-' || exp[i + 1] == '*' || exp[i + 1] == '/' || exp[i + 1] == '^' || exp[i + 1] == '%'||exp[i+1]==')')) {
printf("Error:输入有误,出现无效符,已停止运算\n");
return;
}
if (isdigit(exp[i])) {
int j = i;
double x = 0;
while (j<exp.size() && isdigit(exp[j])) {
x = x * 10 + (exp[j++] - '0');
}
i = j - 1;
nums.push(x);
printf("主要操作:nums.push(%f)\n", x);
Print_num(nums);
}
else if (exp[i] == '(') {
opes.push(exp[i]);
printf("主要操作:opes.push('(')\n");
Print_ope(opes);
}
else if (exp[i] == ')') {
while (opes.top() != '(') {
//先判断是否有数学错误:
//1.进行取模运算但至少有一个数不是整数
//2.进行除法或取模运算时除数为0
//3.进行乘方运算时:底数为0时指数小于或等于0,对负数开偶次方
if (opes.top() == '%') {
double b = nums.top(); nums.pop();
double a = nums.top(); nums.pop();
if (!isInt(a) || !isInt(b)) { printf("Error:取模运算中至少有一个不是整数\n"); return; }
nums.push(a); nums.push(b);
}
if ((opes.top() == '/' || opes.top() == '%') && nums.top() == 0) {
printf("Error:除数不能为0\n");
return;
}
if (opes.top() == '^') {
double b = nums.top(); nums.pop();
double a = nums.top(); nums.pop();
double t = 1.0 / b;
if (a == 0 && b <= 0) { printf("Error:底数为0是,指数必须大于等于0"); return; }
if (a < 0 && b != 0 && !isEven(t)) { printf("Error:负数不能开偶次方\n"); return; }
nums.push(a); nums.push(b);
}
Calculate(nums, opes.top(), oneortwo);
printf("主要操作:opes.pop(),pop的元素是%c\n",opes.top());
opes.pop();
Print_ope(opes);
Print_num(nums);
}
opes.pop(); //左括号出栈
printf("主要操作:opes.pop(),pop的元素是%c\n", opes.top());
Print_ope(opes);
}
else {
if (!isOper(exp[i]) && exp[i] != '#') { printf("输入非法符号,已停止计算\n"); return; }
else {
if (Priority[opes.top()] >= Priority[exp[i]]) {
while ((opes.top() != '#' || exp[i] != '#')&&Priority[opes.top()] >= Priority[exp[i]]) //pop至栈顶元素为左括号并进行相应的计算
{
//先判断是否有数学错误:
//1.进行取模运算但至少有一个数不是整数
//2.进行除法或取模运算时除数为0
//3.进行乘方运算时:底数为0时指数小于或等于0,对负数开偶次方
if (opes.top() == '%') {
double b = nums.top(); nums.pop();
double a = nums.top(); nums.pop();
if (!isInt(a) || !isInt(b)) { printf("Error:取模运算中至少有一个不是整数\n"); return; }
nums.push(a); nums.push(b);
}
if ((opes.top() == '/'||opes.top()=='%') && nums.top() == 0) {
printf("Error:除数不能为0\n");
return;
}
if (opes.top() == '^') {
double b = nums.top(); nums.pop();
double a = nums.top(); nums.pop();
double t = 1.0 / b;
if (a == 0 && b <= 0) { printf("Error:底数为0时,指数必须大于等于0"); return; }
if (a < 0 && b != 0 && !isEven(t)) { printf("Error:负数不能开偶次方\n"); return; }
nums.push(a); nums.push(b);
}
Calculate(nums, opes.top(), oneortwo);
printf("主要操作:opes.pop(),pop的元素为%c\n",opes.top());
opes.pop();
Print_ope(opes);
}
opes.push(exp[i]);
printf("主要操作:opes.push(%c)\n", exp[i]);
Print_ope(opes);
}
else {
opes.push(exp[i]);
printf("主要操作:opes.push(%c)\n", exp[i]);
Print_ope(opes);
}
}
}
}
//输出最终结果
printf("最终结果为:%f\n", nums.top());
}
主函数代码如下:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include "Expr.h"
using namespace std;
int main() {
printf("欢迎使用计算器,该计算器支持四则运算、乘方、单目加减、取模等运算\n");
printf("输入指令ON以开始使用,输入OFF可停止使用并关闭计算器\n");
printf("————————————————————————————————————————————————\n");
string open;
while (cin>>open&& open != "ON") {
if (open == "OFF") {
printf("已停止使用计算器\n");
return 0;
}
printf("输入指令有误,请重新输入指令!\n");
printf("————————————————————————————————————————————————\n");
}
printf("————————————————————————————————————————————————\n");
printf("已开启计算器,请输入算数表达式,输入样例为:1+2*3-4/5+6^7-8%%9#\n");
string expression;
while (cin >> expression) {
if (expression == "OFF") {
printf("已停止使用计算器\n");
return 0;
}
else {
Final(expression);
printf("\n");
printf("————————————————————————————————————————————————\n");
printf("请输入下一条算术表达式以继续计算,否则请输入OFF以关闭计算器\n");
}
}
return 0;
}
第一次发博客,如有不足之处还请大家指出。