中綴表達式求值
1.概述:
在C++ Primer(5th)的第九章順序容器中出現了下面一道習題:
作爲棧適配器的練習,這個題目看上去很複雜(emmmm。。。剛開始看了好幾遍都沒太懂題目要幹什麼)。有些疑惑地我又看了看對應的英文版,意思完全不一樣好不好。。。
現在這個題目就很簡單了,就是通過棧把被左右圓括號括起來的內容替換掉,沒有什麼太多需要注意的地方,實現代碼如下:
#include<iostream>
#include<stack>
#include<string>
using namespace std;
int main(int argc, char *argv[])
{
bool isSeen = false;
string exp = "Hello, ((#))";
stack<char> stk;
for (auto e : exp) {
stk.push(e);
if (e == '(') {
isSeen = true; continue;
}
else if (e == ')' and isSeen) {
while (stk.top() != '(') {
stk.pop();
}
stk.pop();
stk.push('#');
isSeen = false;
}
}
//改一下棧輸出順序
string output;
while (!stk.empty()) {
output.insert(output.begin(), stk.top());
stk.pop();
}
cout << output << endl;.
system("pause");
return 0;
}
這樣題目就完成了,但是畢竟已經看到了中文版的那個翻譯,所以也想試一試能不能搞出來。瞭解了一下,這種要求類似於求解一個簡化的中綴表達式。
所謂中綴表達式就是指我們日常生活中使用的那種表達式形式,這種表達式中操作符位於操作數的中間,比如1+2*3。這種表達式的特點是人類看上去非常直觀,只要規定了相應的運算次序我們就可以很容易的計算出結果。但是對於計算機而言表達式處理起來就困難很多,問題主要產生在下面兩個方面:
- 中綴表達式的操作數位於算符兩邊,因此順序讀入中綴表達式的過程中,看到運算符的那一刻還有一個操作數是未知的,無法完成計算。必須要等到讀入第二個操作數之後纔可以。
- 中綴表達式存在優先級的問題,當讀取到1+2之後不能直接計算,必須要繼續檢查後面是否存在優先級更高的算符,而計算機是並不理解這些規則的,因此實現較爲麻煩。
爲了解決這些問題表達式還有前綴表達式和後綴表達式兩種形式,前者運算符在操作數的前面,而後者運算符在操作數的後面。前綴表達式也成爲波蘭式,後綴表達式也成爲了逆波蘭式。關於前綴和後綴表達式這裏不再敘述,或許留到以後有空再研究,此處只關心如何求解中綴表達式。
2.中綴表達式的解析方法
上面已經敘述了處理中綴表達式中存在的困難,根據上面的理解我們制訂指定下面一些規則來處理中綴表達式:
- 先對錶達式進行預處理,去掉其中的空格,並且將連續存在的多位數字處理爲單一符號。
- 從頭開始遍歷處理後的表達式,將字符依次入棧,我們通過兩個棧來保存表達式中的符號,運算符棧用來保存算符,操作數棧保存表達式中的數字。
- 如果取得的是操作數那麼無條件直接壓入操作數棧。
- 如果取得的是運算符,當運算符棧爲空則直接將符號壓棧,如果棧不爲空則比較當前符號和棧頂符號的優先級。如果當前算符優先級高則正常入棧,如果當前算符優先級低則彈出棧頂運算符和相應個數的操作數進行求值,將結果壓入操作數棧。重複這個步驟直到將當前運算符入棧。
- 當碰到左括號時正常壓入運算符棧。
- 當碰到右括號時則對棧內元素進行求值,彈出操作符和對應個數的操作數,結果壓入操作數棧,該過程重複執行直到碰到上一個左括號並將左括號彈出。
下面列出實現中綴表達式求解的C++代碼,這裏假設可能的運算符包括**+,-,*,/,(,)**,限定操作數都是整數。
#include<iostream>
#include<vector>
#include<string>
#include<cctype>
#include<stack>
using namespace std;
string trimSpace(string rawExp);
vector<string> preParseExp(string exp);
int finalCalc(int l, string opt, int r);
int infixAna(vector<string> &tokens);
int getPriority(string token);
int calculate(stack<int> &opd, string opt);
int main(int argc, char*argv[])
{
string rawExpression("(- 1 + 2* (9 - 3) )* (1 5- 8- 68 /4 *1 +(- 1)-2 *2)");
cout << "表達式正確結果:" << endl;
cout << (-1 + 2 * (9 - 3)) * (15 - 8 - 68 / 4 * 1 + (-1) - 2 * 2) << endl;
//cout << trimSpace(rawExpression) << endl;
//rawExpression.erase();
//cout << rawExpression << endl;
string expression = trimSpace(rawExpression);
cout << expression << endl;
vector<string> parsedExp = preParseExp(expression);
cout << "符號處理後:" << endl;
for (auto e : parsedExp) {
cout << e << " ";
}
cout << endl;
int result = infixAna(parsedExp);
cout << result << endl;
cout << endl;
system("pause");
return 0;
}
string trimSpace(string rawExp)
{
for (auto current = rawExp.begin();
current != rawExp.end();)
{
if (*current == ' ') {
current = rawExp.erase(current);
}
else {
++current;
}
}
return rawExp;
}
vector<string> preParseExp(string exp)
{
vector<string> tokens;
string token;
char prev;
//符號分揀
for (auto current = exp.begin(); current != exp.end();current++) {
token.erase();
switch (*current) {
case '+':
case '*':
case '/':
case '(':
case ')':
token.push_back(*current);
tokens.push_back(token);
break;
case '-':
prev = *(current - 1);
//用作減號
if (prev == ')' or isdigit(prev)) {
token.push_back(*current);
tokens.push_back(token);
break;
}
//用作負號
else {
token.push_back('#');
tokens.push_back(token);
break;
}
default:
//auto currBack = current;
for (; current < exp.end(); ++current) {
if (isdigit(*current)) {
token.push_back(*current);
}
else {
break;
}
}
tokens.push_back(token);
current = current - 1;
//current = currBack;
break;
}
}
return tokens;
}
int getPriority(string token)
{
//數越大優先級越高
int priority;
if (token == "#")
priority = 3;
else if (token == "*" || token == "/")
priority = 2;
else if (token == "+" || token == "-")
priority = 1;
else if (token == "(")
priority = 0;
return priority;
}
int infixAna(vector<string> &tokens)
{
stack<string> operatorStack;
stack<int> operandStack;
int result;
//參數入棧
for (auto &token : tokens) {
if (token == "*" or token == "/" or token == "+" or token == "-" or token == "#") {
if (operatorStack.empty()) {
operatorStack.push(token);
}
else {
string topOpt = operatorStack.top();
if(getPriority(token) > getPriority(topOpt)) {
operatorStack.push(token);
}
else {
while (getPriority(token) <= getPriority(topOpt = operatorStack.top())) {
operatorStack.pop();
result = calculate(operandStack, topOpt);
operandStack.push(result);
}
operatorStack.push(token);
}
}
continue;
}
else {
if (token == "(") {
operatorStack.push(token);
continue;
}
else {
if (token == ")") {
string topOpt;
while ((topOpt = operatorStack.top()) != "(") {
operatorStack.pop();
result = calculate(operandStack, topOpt);
operandStack.push(result);
}
operatorStack.pop();
continue;
}
//數字
else {
operandStack.push(stoi(token));
continue;
}
}
}
}
while (operatorStack.size() != 0)
{
string topOpt = operatorStack.top();
result = calculate(operandStack, topOpt);
operandStack.push(result);
operatorStack.pop();
}
result = operandStack.top();
operandStack.pop();
return result;
}
int finalCalc(int l, string opt, int r)
{
int result;
switch (opt[0]) {
case '+':
result = l + r;
break;
case '-':
result = l - r;
break;
case '*':
result = l * r;
break;
case '/':
result = l / r;
break;
case '#':
result = l - r;
break;
}
return result;
}
int calculate(stack<int> &opd, string opt)
{
int r, l, result;
if (opt == "#") {
r = opd.top();
opd.pop();
result = finalCalc(0, opt, r);
}
else {
r = opd.top();
opd.pop();
l = opd.top();
opd.pop();
result = finalCalc(l, opt, r);
}
return result;
}