完成度更高的有括號版本C++實現四則運算器(有括號)
對於無括號的計算器,實現起來比較容易,下面讓我們一步步實現。
舉例
首先明確需要實現怎樣的程序,對於無括號的計算器,大概做成這樣就可以了:
52+34*3-4/2=
分析
對於例子中的表達式,由於乘除運算的優先級高於加減運算,我們不能直接從左到右進行。但四則運算的規則是從左到右,先乘除后加減,對於優先級相同的運算符還是可以從左到右運算的。
因此我們可以每讀到一個運算符時,檢查前一個運算符的優先級,如果前一個運算符的優先級與當前運算符相等或更高,那么我們便可以完成前一個運算符的計算;反之,則不進行運算。這樣一來就需要將之前的運算符以及運算符左右的數保存起來,由於我們每次都是取前一個運算符,符合后進先出的條件,故可以選擇棧來存儲數據和符號。最好將數據和符號分開存儲,這里為了簡便(整數棧即可存儲數字也可存儲字符),只實現整數的四則運算,若需要浮點數的運算,稍加修改即可。
首先,實現一個棧的類,或者直接使用STL
//Stack.h
#ifndef STACK_H
#define STACK_H
#include<iostream>
class stack_int
{
private:
int* bottom; //棧底
int* top; //棧頂
unsigned int capacity;//棧容量
unsigned int size; //棧大小
public:
stack_int() :bottom(new int[11]), top(bottom), capacity(10), size(0) {};
stack_int(unsigned int capacity) :bottom(new int[capacity+1]),top(bottom), capacity(capacity),size(0){};
int operator[](unsigned int i) const
{
return *(bottom + i);
}
bool isEmpty()const { return bottom == top; }
bool isFull()const { return size == capacity-1; }
unsigned int getsize()const { return size; }
unsigned int getcapacity()const { return capacity; }
int gettop()const
{
if (!isEmpty())
return *(top - 1);
else
return -1;
}
void settop(int i)
{
if (!isEmpty())
{
*(top - 1) = i;
}
}
void push(int i)
{
if ((top - bottom)<capacity)
{
*top = i;
top++;
size++;
}
else
{
std::cout << "stack full!" << std::endl;
stack_expansion();
push(i);
}
}
int pop(int &val)
{//返回值為1則棧未空,返回值為0則棧已空無法出棧
if (top > bottom)
{
top--;
size--;
val = *top;
return 1;
}
else
{
std::cout << "stack empty!" << std::endl;
return NULL;
}
}
private:
void stack_expansion()
{//棧擴容
std::cout << "正在擴容中..." << std::endl;
int newcapacity = 2 * capacity + 1;
int* newbottom = new int[newcapacity + 1];
int* newtop = newbottom;
for (int i = 0; i < size; ++i)
{
*newtop = *bottom;
newtop++;
bottom++;
}
bottom = newbottom;
top = newtop;
capacity = newcapacity;
}
};
#endif
然后在我們的主程序中利用棧來分析四則運算的規律(源代碼如下)
//Main.cpp
#include"stack.h"
#include<iostream>
using namespace std;
bool is_digit(char i)
{//是數字
if (i == '1' || i == '2' || i == '3' || i == '4' || i == '5' || i == '6' || i == '7' || i == '8' || i == '9' || i == '0')
return true;
else return false;
}
bool is_operator(char i)
{//是運算符
if (i == '+' || i == '-' || i == '*' || i == '/' ||i=='=')
return true;
else return false;
}
bool get_priority(char pre,char cur)
{//獲取兩個符號間的優先級,pre為靠前的字符,cur為靠后的字符
if ((pre == '+' || pre == '-') && (cur == '*' || cur == '/'))
return false;
else
return true;
}
int do_operation(int lnum, char ope, int rnum)
{
if (ope == '+')
return lnum + rnum;
if (ope == '-')
return lnum - rnum;
if (ope == '*')
return lnum * rnum;
if (ope == '/')
return lnum / rnum;
}
/*
1+2*3=
1+5*4-345+36/6*4+145*4*5-52=
*/
int main()
{
stack_int s;
stack_int num_stack;//數據棧
stack_int ope_stack;//符號棧
char current_char;
current_char = getchar();
bool overflag = false;
while (overflag!=true)
{//未遇到=號時不斷進行四則運算
if (is_digit(current_char))
{//遇到數字符號則將完整的數解析出來並保存於棧中
int num = 0;
num = current_char - '0';//符號轉數字
current_char = getchar();//獲取下一個字符
while (is_digit(current_char))
{
num = num * 10+(current_char-'0');
current_char = getchar();
}
num_stack.push(num);
//cout <<"the number is " <<num << endl;
}
if (current_char == ' ')
{//空格則繼續
current_char = getchar();
continue;
}
if (is_operator(current_char))
{//遇到運算符則將運算符保存於運算符棧中
int ope = '?';
//如果當前符號棧非空,則不斷根據優先級決定是否進行一次運算
while((!ope_stack.isEmpty())&&(get_priority((char)ope_stack.gettop(),current_char)))
{//如果前一個運算符優先級更高
ope_stack.pop(ope);
//cout << "找到了前一個運算符為: " << (char)ope << endl;
int lnum, rnum;
//符號棧非空時,數據棧應該至少有兩個數,否則出錯
if (num_stack.isEmpty())
{
cout << "數據棧缺失兩個元素,解析失敗!" << endl;
overflag = true;
break;
}
num_stack.pop(rnum);
if (num_stack.isEmpty())
{
cout << "數據棧缺失一個元素,解析失敗!" << endl;
overflag = true;
break;
}
num_stack.pop(lnum);
lnum = do_operation(lnum, (char)ope, rnum);//進行運算
num_stack.push(lnum);
}
if (current_char == '=')
{//如果解析到=號了,解析完成
overflag = true;
break;
}
ope_stack.push(current_char);
current_char = getchar();
}
}
for (int i = 0; i < num_stack.getsize(); ++i)
cout << num_stack[i] << "\t";
cout << endl;
for (int i = 0; i < ope_stack.getsize(); ++i)
cout << (char)ope_stack[i] << "\t";
return 0;
}
這里需要注意一些問題,首先,由於整數可能是多位數,因此在遇到一個數字符號時,我們可以通過循環將后面幾位全部找出,並將符號轉化為真正的數值。
第二個問題就是有時候會出現表達式解析到等號了,卻有很多數沒進行運算,解決這個問題的方法就是在
if (is_operator(current_char))
中使用while循環,並將循環條件設置為棧非空且棧頂運算符優先級高於當前讀入的運算符(前提是=的優先級小於任何運算符)
while((!ope_stack.isEmpty())&&(get_priority((char)ope_stack.gettop(),current_char)))