实验三 逆波兰式的产生及计算
一、实验目的
将用中缀式表示的算术表达式转换为用逆波兰式表示的算术表达式,并计算用逆波兰式来表示的算术表达式的值
二、实验题目
如输入如下: 21 + ( ( 42 − 2 ) ∗ 15 + 6 ) − 18 21+((42-2)* 15+6)-18 21+((42−2)∗15+6)−18 #
输出为:
原来表达式: 21 + ( ( 42 − 2 ) ∗ 15 + 6 ) − 18 21+((42-2)* 15+6)-18 21+((42−2)∗15+6)−18
后缀表达式: 21 42 2 − 15 ∗ 6 + + 18 − 21\space 42 \space 2 \space - \space 15 \space *\space 6 \space +\space + \space18\space- 21 42 2 − 15 ∗ 6 + + 18 −
计算结果: 609 609 609
三、编码实现
源代码
#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <map>
#include <cmath>
std::vector<std::string> ans;
std::map<char, int> priority; // define the prioritization
/*
* these operators will be accept
* . should not be seen as a opt here
*/
std::string opt = "+-*/()^%";
void trans(std::string str) {
std::stack<char> ops;
// insert a '#' in the rear side of stack symbolized the end
ops.push('#');
// storing opts temporarily
std::string tempStr;
std::string tempFlag;
读取数字
对于由数字或符号开始的字符串,逐个字符读取以获取数字,直到下个符号不是数字 1 ∼ 9 1 \sim 9 1∼9 或小数点 . . . 为止,从而实现对小数和负数的读取。
对于负数,视作单目运算符 − - − 与一个正数的复合,记作 @ @ @, @ @ @ 符号放入字符串尾部。
for (int i = 0; i < str.size(); i++) {
/*
* check opt whether a true opt or a part of a number
* if i equal to zero,the opt + & - should be seen as a part of number except this is the end
*/
if (((str[i] == '-' || str[i] == '+' || str[i] == '.') &&
(i == 0 || std::string("+-/*(^%").find(str[i - 1]) != std::string::npos)) ||
isdigit(str[i])) { // is number
tempStr = str[i] != '+' ? str.substr(i, 1) : "";
while (i + 1 < str.size() && opt.find(str[i + 1]) == std::string::npos) { // border control and
tempStr += str[++i];
}
if (tempStr[0] == '-') {
tempStr.erase(0, 1);
tempFlag = "@";
} else {
tempFlag = "";
}
ans.push_back(tempStr);
if (!tempFlag.empty()) {
ans.push_back(tempFlag);
tempFlag.clear();
}
} else {
// for operators
if (str[i] == '(')
ops.push(str[i]);
else if (str[i] == ')') {
while (ops.top() != '(') {
ans.emplace_back(1, ops.top());
ops.pop();
}
ops.pop();
} else {
while (priority[str[i]] <= priority[ops.top()] && str[i] != '^') {
ans.emplace_back(1, ops.top());
ops.pop();
}
ops.push(str[i]);
}
}
}
while (ops.size() > 1) {
ans.emplace_back(1, ops.top());
ops.pop();
}
}
double cal() { //calculator
std::stack<double> temp;
for (const auto &i: ans) {
try {
// turn string to double
temp.push(std::stod(i));
} catch (...) {
/*
* exception occurred, which means this is not a num but opt
* we can start cal here
*/
char c = i[0];
double res = 0;
if (c == '@') {
res = res - temp.top();
temp.pop();
temp.push(res);
} else if (c == '+') {
res = res + temp.top();
temp.pop();
res = res + temp.top();
temp.pop();
temp.push(res);
} else if (c == '-') {
res = res + temp.top();
temp.pop();
res = temp.top() - res;
temp.pop();
temp.push(res);
} else if (c == '*') {
res = res + temp.top();
temp.pop();
res = res * temp.top();
temp.pop();
temp.push(res);
} else if (c == '/') {
res = res + temp.top();
temp.pop();
res = temp.top() / res;
temp.pop();
temp.push(res);
} else if (c == '^') {
res = res + temp.top();
temp.pop();
res = pow(temp.top(), res);
temp.pop();
temp.push(res);
} else if (c == '%') {
res = res + temp.top();
temp.pop();
res = fmod(temp.top(), res);
temp.pop();
temp.push(res);
}
}
}
return temp.top();
}
加入乘方运算符号 ^,拥有最高优先级
加入取余符号 % \% %,优先级同乘除号
int main() {
priority['^'] = 4;
priority['*'] = priority['/'] = priority['%'] = 3;
priority['+'] = priority['-'] = 2;
priority['('] = 1;
priority['#'] = 0;
std::string str;
getline(std::cin, str);
trans(str);
for (int i = 0; i < ans.size(); i++)
std::cout << (i ? " " : "") << ans[i];
std::cout << std::endl;
double res_n = cal();
std::cout << " " << '=' << " " << res_n << std::endl;
return 0;
}
改进与提升小结
增加了对取余运算、乘方运算、负数(负号视作单目运算符,在此代码中记作 @ @ @ )的支持
四、运行测试
-
基本要求(四则运算与括号)
23 + ( 34 ∗ 5 / 2 − 1 ) 23+(34*5/2-1) 23+(34∗5/2−1)
-
含负数的处理
− 34 + 7 ∗ 5 -34+7*5 −34+7∗5
-
含乘方的处理
4 3 2 4^{3^{2}} 432
-
需取余的处理
34 % 8 % 3 34\%8\%3 34%8%3
-
综合测试
( − 34 + 3 2 2 / 9 % 4 ) + 8 ∗ ( 3.6 − 4 ) (-34+3^{2^2}/9 \%4)+8*(3.6-4) (−34+322/9%4)+8∗(3.6−4)