科學計算器學生時代小作品原始碼(C++版)

淡遠發表於2020-10-06

/*************************************************************************************

                                                                 簡易科學計算器

                                                                           made by:danyuan

                                                                           專業:12級——資訊工程2班

                                                                           學校:湖南理工學院

//功能說明:

         1.可以進行帶括號的表示式運算;

         2.可以檢查並識別更改表示式錯誤:

                   (1.對於非法字元,報錯處理

                   (2.對於括號不匹配,報錯處理

                   (3.對於數字或右括號接左括號,自動在中間補乘號

         3.可以進行負數運算;

         中綴表示式規則///

         操作符特性:

         0.表示式第一個字元不能為*、/、^、!、)

         1.+、-、*、/這類操作符不能連續兩個在一起,後面不能接')'、'!'

         2.'('後面不能接+、*、/、!這些符號,如果後面是‘-’,則認為是負號

         3.'!'後面不能接數字,可以接其它操作符,如果不是!和^則在中間自動補乘號

         4.'^'後面不能接任何操作符除了‘(’

         5.單向右操作符後面只能接同類操作符或數字、(。例如:開方、取反、求倒數、三角函式等

         6.單向左操作符後面不能接數字,如果是同類操作符繼續操作,如果是‘(’或其他操作符中間新增乘號

         7.括號必須配對

 

  總結:大體目前可以分為四類:

 

                   即:接雙運算元的操作符、接左運算元的單操作符、接右運算元的單操作符、雙向都可接的單操作符(此類

 

暫不談論,看情況而定)

*************************************************************************************/

先上圖:

當然這是美化後的科學計算器,下面是基礎計算器的程式碼:

#ifndef CACULATOR_H

#define CACULATOR_H

#include "DYHeaderFile/stack.h"

 

template<class T>

class DYCaculator{

protected:

         char *middlestr;//中綴式字串

         char *backstr;//字尾式字串

         T m_result;       //存放運算結果

         stack<T> numstack;//用於存放運算元的棧

         stack<char> operatestack;//用於存放操作符號的棧

 

         static        char m_symbolarr[255];//基本計算器能識別的符號

public:

         /工具類函式///

         static T factoria(T num);//階乘,特點:接左運算元的操作符!表示和^同級

 

         /框架計算函式/

         DYCaculator(char *str);

         DYCaculator()//過載建構函式

         {

                   middlestr=NULL;

                   backstr=NULL;

                   m_result=0;

         }

         ~DYCaculator();

         virtual bool SetText(char * a);

         T GetResult();

         bool GetBackStr();

         virtual bool Caculating();

         virtual T DealFun(char c);

         virtual bool CheckOperator();

         virtual void SetSinFlag(bool flag)=0;//設定角度還是弧度運算

         //工具函式/

         virtual int GetClass(char a);//返回分類

         int JungdeLevel(char a);//判斷優先順序

         int GetNumClass(char a);//返回分類,只為GetNum函式服務

         T GetNum(char *str);//根據字串返回其數值

         void IverseStr(char *str);

         void Ttostr(T num,char *str);//從某個數值轉換為字串

         bool strequal(char *a,char *b);//忽略大小寫的字串判等函式

};

//工具函式

template<class T>

int DYCaculator<T>::GetClass(char a)//返回分類

{

         char arr1[]="1234567890";

         char arr2[]="+-*/()^!";//同類不能疊加(特殊另作處理),後可以接子類操作符

         int flag=-1,i;

 

         for(i=0;i<(int)(strlen(arr1));i++)

                   if(a==arr1[i])

                            flag=1;//表明是數字

 

         if(a=='.')

                   flag=2;//表明是小數點

        

         for(i=0;i<(int)(strlen(arr2));i++)

                   if(a==arr2[i])

                            flag=3;//表明是操作符

         //子類可重寫單操作符的處理

 

         return flag;

}

template<class T>

char DYCaculator<T>::m_symbolarr[255]="0123456789+-*/()^.!";//abcdefghijklmnopqrstuvwxyz//a~z代表可以支援26個附

 

加操作或函式

template<class T>

DYCaculator<T>::DYCaculator(char *str)//生成物件時,傳入一個操作指令,進行分配記憶體、獲得操作指令

{

         middlestr=new char[strlen(str)*2];

         strcpy(middlestr,str);

         backstr=new char[2*strlen(str)];

         backstr[0]='\0';

         m_result=0;

}

 

template<class T>

bool DYCaculator<T>::SetText(char * str)//後面呼叫,傳入一個操作指令,進行分配記憶體、獲得操作指令

{

         if(middlestr)//如果原來已經分配了記憶體

                   delete []middlestr;//釋放原有記憶體

         if(backstr)

                   delete []backstr;

         middlestr=new char[strlen(str)*2];

         if(!middlestr)

                   return false;//若分配記憶體失敗,則報錯

         strcpy(middlestr,str);

         backstr=new char[2*strlen(str)];

         if(!backstr)

                   return false;//若分配記憶體失敗,則報錯

         backstr[0]='\0';//清空該字串

         m_result=0;//初始化

         numstack.clear();

         operatestack.clear();

         return true;

}

template<class T>

DYCaculator<T>::~DYCaculator()//釋放動態分配的記憶體

{

         delete []middlestr;

         delete []backstr;

}

 

template<class T>

bool DYCaculator<T>::CheckOperator()//檢驗輸入表示式合法性

{

         stack<char> teststack;//匹配'('、')'

         int i=0,j=0;

         int len=strlen(middlestr);

         int symbolstrlen=strlen(m_symbolarr);

         bool chflag=false;

         //0.第一個字元不能為*、/、^、!、)

         char arr[6]="*/^!)";

         for(i=0;i<5;i++)

                   if(middlestr[0]==arr[i])

                            return false;

         for(i=0;i<len;i++)

         {

                   //1.檢查是否有非法字元出現在表示式中

                   chflag=false;//合法符號標誌位

                   for(int m=0;m<symbolstrlen;m++)

                            if(middlestr[i]==m_symbolarr[m])

                                     chflag=true;

                   if(!chflag)

                            return false;

                   //2.利用棧判斷括號是否配對

                   if(middlestr[i]=='(')

                            teststack.push('(');

                   if(middlestr[i]==')')

                   {

                            if(teststack.empty())//如果沒有左括號,則表示式有誤

                                     return false;

                            teststack.pop();//彈出'('

                   }       

                   //3.自動新增乘號,即:1.')'緊接'('、 2.數字緊接'('、 3.')'接數字、4.數字接右單操作符, 中間自動補

 

乘號

                   int addflag=false;

                   if(GetClass(middlestr[i])==1||(middlestr[i]==')'))//如果是數字

                   {

                            if(middlestr[i+1]=='(')//||((middlestr[i]==')')&&(GetClass(middlestr[i+1])==1))||

 

((GetClass(middlestr[i+1])==4)))//則判斷為乘號,自動為表示式新增乘號

                                     addflag=true;

                            if(middlestr[i]==')'&&(GetClass(middlestr[i+1])==1||(GetClass(middlestr[i+1])==4)))//右

 

括號後接數字或左單向操作符添乘號

                                     addflag=true;

                            if(GetClass(middlestr[i])==1&&(GetClass(middlestr[i+1])==4))

                                     addflag=true;

                   }

                   if(middlestr[i]=='!'&&(GetClass(middlestr[i+1])==1||(middlestr[i+1]=='(')))//'!'後面接數字和'('

 

自動補乘號

                   {

                            addflag=true;

                   }

                   //為有需要的地方新增乘號,屬於步驟3

                   if(addflag)

                   {

                            int k=0;

                            for(k=len;k>i;k--)//元素後移,騰出空位

                            {

                                     middlestr[k+1]=middlestr[k];

                            }

                            middlestr[++k]='*';//新增乘號

                            middlestr[++len]='\0';

                   //      cout<<middlestr<<endl;//測試之用

                   }

 

                   //4.檢查是否有操作符緊接操作符(預判操作i+1),即:四則運算子之間要隔開、四則運算子不能接')'等

 

                   if(GetClass(middlestr[i])==3)//如果是操作符

                   {

                            if(middlestr[i]=='!')

                            {

                                     if(GetClass(middlestr[i+1])==1)//階乘符號後面不能接數字

                                               return false;

                            }

                            else if(middlestr[i]=='(')//左括號後面不可直接接雙操作符

                            {

                                     if(GetClass(middlestr[i+1])==3&&(middlestr[i+1]!='(')&&(middlestr[i+1]!=')')&&

 

(middlestr[i+1]!='-'))

                                               return false;

                            }

                            else if(middlestr[i]==')')//右括號所有操作符都可以接

                            {

                           

                            }

                            else

                            {

                                     if(GetClass(middlestr[i+1])==3&&(middlestr[i+1]!='('))

                                               return false;//如果四則運算子號後面緊接四則運算子號,則表示式有誤

                            }

                   }

 

                   //5.如果是單操作符,方式又不一樣了

                   if(GetClass(middlestr[i])==4)

                   {

                            //1.單操作符後不能接操作符(除‘(’)

                            if(GetClass(middlestr[i+1])==3&&(middlestr[i+1]!='(')||(GetClass(middlestr[i+1])==4))

                                     return false;

                   }

        

         }

         if(!teststack.empty())

                   return false;//如果棧不為為空,則配對失敗,此操作屬於步驟2

 

         return true;

}

 

template<class T>

T DYCaculator<T>::GetResult()

{

         cout<<"有什麼不懂的,或是想交流程式設計心得的人";

         cout<<endl<<"歡迎大家"<<"加我"<<"企鵝群:320540648"<<endl;

         return m_result;

}

 

template<class T>

bool DYCaculator<T>::GetBackStr()//獲得字尾表示式

{

         int i=0,j=0,flag=0;

         int len=strlen(middlestr);

         int reallen=0;//字尾式字串實際長度

         bool separetflag=0;

         for(i=0;i<len;i++)

         {

                   flag=GetClass(middlestr[i]);

                   if(flag==3||(flag==4))//一. 如果是操作符,就判斷優先順序入棧或出棧

                   {

                            //(1.分隔兩個運算元字串

                            if(separetflag==1)//如果前面有數字,新增分隔符

                            {

                                     backstr[reallen++]='|';

                                     separetflag=0;

                            }

                            //(2.對操作符優先順序的判斷,即:

                                               //1.前後兩操作符之間的優先順序判斷(前者先出棧,若後者優先順序高,則後者入

 

棧,否者前者出棧);

                                               //2.若遇到左括號,直接入棧(優先順序最高),遇到右括號,操作符連續出棧,

 

直到彈出一個左括號為止,彈出的四則運算子新增到字尾式末尾;

                                               //3.智慧識別負號,用#號代替負號

                            if(operatestack.empty())//如果操作符棧為空,則直接入棧

                                     operatestack.push(middlestr[i]);

                            else

                            {

                                     if(middlestr[i]=='(')//如果是左括號,且後面是減號,則為後面的數新增一個負號

                                     {

                                               operatestack.push(middlestr[i]);//先入棧

                                               if(middlestr[i+1]=='-')

                                               {

                                                        backstr[reallen++]='#';//代表這是負號,不是減號

                                                        i++;

                                                        continue;

                                               }

                                     }

                                     else if(middlestr[i]==')')//如果為右括號,則出棧,直到彈出一個左括號為止

                                     {

                                               while(!operatestack.empty()&&(operatestack.gettop()!='('))//依次彈出棧

 

內的符號

                                               {

                                                        backstr[reallen++]=operatestack.gettop();

                                                        operatestack.pop();

                                               }

                                               operatestack.pop();//彈出'('

                                     }

                                     else

                                     {

                                               //如果棧內符號優先順序大於當前符號,則依次出棧

                                               while(!operatestack.empty()&&(JungdeLevel(operatestack.gettop())

 

>=JungdeLevel(middlestr[i]))&&(operatestack.gettop()!='('))

                                               {

                                                        backstr[reallen++]=operatestack.gettop();

                                                        operatestack.pop();

                                               }

                                               //否則就入棧

                                               operatestack.push(middlestr[i]);

                                     }

                            }

                   }

                   else//二.是運算元中的字元

                   {

                            backstr[reallen++]=middlestr[i];

                            separetflag=1;

                   }

         }

         while(!operatestack.empty())//如果棧內還有操作符,則依次全部出棧

         {

                   backstr[reallen++]=operatestack.gettop();

                   operatestack.pop();

         }

         backstr[reallen]='\0';

         return true;

}

template<class T>

bool DYCaculator<T>::Caculating()//計算字尾式並獲得結果

{

         GetBackStr();//轉為字尾式

         int i=0,j=0;

         int pos=0;

         int backreallen=strlen(backstr)+1;

         char arr[100];

         bool numflag=false;

 

         for(i=0;i<backreallen;i++)

         {                

                   //1.如果是操作符,則將棧頂兩個元素取出進行四則運算

                   if(GetClass(backstr[i])==3||(GetClass(backstr[i])==4))//如果是四則運算子號

                   {

                            if(numflag)//如果後面是操作符,則將運算元入棧

                            {

                                     numflag=false;

                                     arr[j]='\0';

                                     numstack.push(GetNum(arr));//根據字串獲得數值並將數值壓入棧中

                                     j=0;//初始化

                            }                          

                            numstack.push(DealFun(backstr[i]));//將運算結果壓入棧中

                   }//2.如果是分隔符或結尾符,判斷其前面是否有運算元字串,如果有就將運算元字串轉化為數字後入棧

                   else if(backstr[i]=='|'||(backstr[i]=='\0'))//如果後面是分隔符或結尾符,將運算元入棧

                   {       

                            if(numflag)//將運算元入棧

                            {

                                     numflag=false;

                                     arr[j]='\0';

                                     numstack.push(GetNum(arr));

                                     j=0;//初始化

                            }

                   }

                   else //3.生成數字字串,即:用一個字串來儲存操作符含有的字元

                   {

                            if(backstr[i]=='#')//代表負數,如果是負數,前面加負號

                            {

                                     arr[j++]='-';

                                     continue;

                            }

                            arr[j++]=backstr[i];//如果是數字,則加入數字字串

                            numflag=true;//代表有數字還未入棧

                   }

         }

 

         m_result=numstack.gettop(); 

         return true;

}

template <class T>

T DYCaculator<T>::DealFun(char c)

{

         bool doubleOrSingle=false;//初始化為雙運算元

         T operate1=0,operate2=0,result=0;

         //判斷是單運算元還是雙運算元.....(單運算元只需彈出一個運算元,否則兩個)

         if(c=='!')

                   doubleOrSingle=true;

        

         if(doubleOrSingle)//單運算元的情況

         {

                   if(c=='!')

                   {

                            result=factoria(numstack.gettop());

                            numstack.pop();

                   }

         }

         else//此處是雙運算元的情況

         {

                   if(!numstack.empty())

                            {

                                     operate1=numstack.gettop();

                                     numstack.pop();

                            }

                            else

                            {

                                     if(c=='-'||(c=='+'))

                                               operate1=0;

                                     else

                                               operate1=1;

                                    

                            }

                            if(!numstack.empty())

                            {

                                     operate2=numstack.gettop();

                                     numstack.pop();

                            }

                            else

                            {

                                     if(c=='-'||(c=='+'))

                                               operate2=0;

                                     else

                                     {

                                               operate2=operate1;

                                               operate1=1;

                                     }

                                    

                            }

                            switch(c)

                            {

                            case '-':result=operate2-operate1;break;

                            case '+':result=operate2+operate1;break;

                            case '*':result=operate2*operate1;break;

                            case '/':

                                     if(operate1!=0)

                                               result=operate2/operate1;

                                     else

                                               return 0;//分母為零時操作失敗

                                     break;

                            case '^':result=pow((double)operate2,(double)operate1);break;

                   }

         }

                            return result;

}

//計算功能函式//

template<class T>

T DYCaculator<T>::factoria(T num)//階乘,特點:接左運算元的操作符!表示和^同級

{

         if(num<=0)

                   return (T)0;

         if(num==1)

                   return 1;

         for(int i=(int)(num-1);i>0;i--)

                   num*=(T)i;

         return num;

}

//工具函式//

template<class T>

int DYCaculator<T>::JungdeLevel(char a)//判斷優先順序

{

         char arr[]="abcdefghijk";//比*、/優先順序高

         for(int i=0;i<(int)(strlen(arr));i++)

                   if(a==arr[i])

                            return 3;

         if(a=='+'||(a=='-'))

                   return 0;

         else if(a=='*'||(a=='/'))

                   return 2;

         else if(a=='^'||a=='!')

                   return 4;

         else

                   return 5;//否者為括號,為最高階

}

 

template<class T>

int DYCaculator<T>::GetNumClass(char a)//返回分類,只為GetNum函式服務

{

         char arr1[]="1234567890";

         char arr2[]=".";

         int flag=-1,i;

 

         for(i=0;i<(int)(strlen(arr1));i++)

                   if(a==arr1[i])

                            flag=1;//表明是數字

 

         if(a=='.')

                   flag=2;//表明是小數點

        

         return flag;

}

template<class T>

T DYCaculator<T>::GetNum(char *str)//根據字串返回其數值

{

         T result=0;

         T t1=0,t2=0;

         int len=strlen(str);

         int i=0,flag=0,j=1;

        

         for(i=0;i<len;i++)

         {

                   if(GetNumClass(str[i])==2)

                            flag=1;

                   if(GetNumClass(str[i])==1)

                   {

                            if(!flag)

                                     t1=t1*10+(str[i]-'0');

                            else

                            {

                                     t2=t2*10+(str[i]-'0');

                                     j*=10;//判斷小數點向後移動的位數

                            }

                   }       

         }

         result=t1+(t2/j);//整數部分加上小數部分

         if(str[0]=='-')

                   result*=-1;

         return result;

}

template<class T>

void DYCaculator<T>::IverseStr(char *str)//字串翻轉

{

         int i=0,len=strlen(str);

         char c;

         for(i=0;i<((len+1)/2);i++)

         {

                   c=str[i];

                   str[i]=str[len-i-1];

                   str[len-i-1]=c;

         }

                  

}

template<class T>

void DYCaculator<T>::Ttostr(T num,char *str)//從某個數值轉換為字串,存在精度損失,不可取

{

         int i=0,j=0;

         int tnum=(int)num,tmod=1;

         while(tnum>0)

         {

                   str[i++]=tnum%10+'0';//返回字元

                   tnum/=10;

         }

         str[i]='\0';

         IverseStr(str);

         str[i++]='.';

         for(j=0;j<6;j++)//精確到小數點後6位即可

         {

                   tnum=(int)(num*10)%10;

                   str[i++]=tnum+'0';

                   num*=10;

         }

         str[i]='\0';

}

template<class T>

bool DYCaculator<T>::strequal(char *a,char *b)//忽略大小寫的字串判等函式

{

         int i=0,j=0;

         bool flag=true;

         if(strlen(a)!=strlen(b))//如果長度不等,則不相等

                   return false;

         for(i=0;a[i]!='\0'&&(b[i]!='\0');i++)

         {

                   //忽略大小寫進行比較

                   if(a[i]!=b[i]&&(a[i]!=(b[i]+('A'-'a')))&&(a[i]!=(b[i]-('A'-'a'))))

                   {

                            return false;

                   }

         }

         return true;

}

#endif

相關文章