將算數表示式轉換成字尾表示式並計算結果

BruceeChou發表於2018-05-27

背景    

 

  用計算機實現帶括號的四則運算的方式。這裡的困難在於乘除運算的優先順序高於加減運算,並且加入了括號,使得問題變得更加困難。20世紀50年代,波蘭邏輯學家想到了一種不需要括號的字尾表達法,我們也把它稱為逆波蘭表示。比如:9+(3-1)*3+10/2,如果用字尾表示法就是9 3 1 - 3 * + 10 2 / +,這樣的表示式稱為字尾表示式,叫字尾的原因在於所有的符號都是要在運算數字的後面出現。

  假設算數表示式只包含“+”、“-”、“×”、“÷”、正整數和括號的合法數學表示式,這稱為簡單算數表示式。對該表示式求值的過程是:先將算數表示式轉換成字尾表示式(亦稱為逆波蘭表示式),然後對該字尾表示式求值。

  為什麼要這樣做呢?人可以通過看一眼就知道先乘除後加減進行計算,但是計算機不可以。如果轉換成字尾表示式,就已經把運算次序考慮過了,計算機在進行運算的時候就不需要考慮運算的次序。

建議

  本篇部落格大概有170行程式碼,如果逐行閱讀,會有一種雲裡霧裡的感覺,建議先去體會如何利用字尾表示式求值,可以在演草紙上寫寫畫畫,理解了使用字尾表示式相對算數表示式(中綴表示式)的好處之後,再仔細揣摩字尾表示式是如何得到的。程式碼量一旦增加,閱讀他人的程式碼,就要由頂至下去閱讀,逐行閱讀費時費力,而且不是很簡單讀懂他人的程式碼。

#include<stdio.h>
#include<stdlib.h>
#define MaxSize 100

  

  巨集定義NUL為字串結束符:

#define NUL '\0'

  

  運算子棧用於存放字尾表示式(重點):

/*運算子棧型別*/
typedef struct
{
	char data[MaxSize];
	int top;
}OP;

  

  數值棧用於存放數值

/*數值棧型別*/
typedef struct
{
	float data[MaxSize];
	int top;
}ST;

 

trans函式編寫思路如下:遍歷字串exp

 

  • 字元ch為數字,將後繼所有數字字元均依次存放到postexp中,並以字元"#"標誌數字串的結束
  • 字元ch為左括號"(",將此括號進棧到運算子棧op中
  • 字元ch為右括號")",將運算子棧op中左括號"("以前的運算子依次出棧,並將左括號刪除
  • 字元ch的運算子優先順序不大於運算子棧op的棧頂運算子(除了棧頂運算子為左括號“(”外)的優先順序,則依次出棧並存入到postexp中,然後將字元ch進棧
  • exp掃描完畢,將運算子棧op中所有的運算子依次出棧並存放到postexp中,得到字尾表示式postexp
/*****************************************************************
*函式名:trans
*函式功能描述:將中綴表示式exp轉化為字尾表示式postexp(也稱逆波蘭表示式)
*函式引數:exp[]-存放中綴表示式的字元陣列,postexp[]-存放字尾表示式的字元陣列
*函式返回值:無返回值
*作者:王賦睿
*函式建立日期:2018.5.27
*函式修改日期:尚未修改
*修改人:尚未修改
*修改原因:尚未修改
*版本:1.0
*歷史版本:無
*****************************************************************/
void trans(char exp[], char postexp[])
{
	char ch;
	int i = 0, j = 0;
	OP op;
	op.top = -1;//初始化運算子棧

	ch = exp[i]; i++;
	while (ch != NUL)
	{
		switch (ch)
		{
		case '(':
			op.top++;
			op.data[op.top] = ch;
			break;
		case ')':
			while (op.data[op.top] != '(')
			{
				postexp[j++] = op.data[op.top--];
			}
			op.top--;
			break;
		case '+':
		case '-':
			while (op.top != -1 && op.data[op.top] != '(')
			{
				postexp[j++] = op.data[op.top--];
			}
			op.top++; op.data[op.top] = ch;
			break;
		case '*':
		case '/':
			while (op.top != -1 && op.data[op.top] != '(' && (op.data[op.top] == '*' || op.data[op.top] == '/'))
			{
				postexp[j++] = op.data[op.top--];
			}
			op.top++;
			op.data[op.top] = ch;
			break;
		case ' ':
			break;//過濾掉空格
		default:
			while (ch >= '0'&&ch <= '9')
			{
				postexp[j++] = ch;
				ch = exp[i++];
			}
			i--;
			postexp[j++] = '#';
			break;
		}
		ch = exp[i++];
	}

	while (op.top != -1)
	{
		postexp[j++] = op.data[op.top--];
	}

	postexp[j] = NUL;
}

  

  針對字尾表示式進行求值:遍歷字尾表示式postexp:

 

  • 字元ch為數字,將後繼的所有數字字元構成一個整數存放到數值棧st中
  • 字元ch為"+"或"-",則從數值棧st中退棧兩個運算數,相加(減)後進棧數值棧st中
  • 字元ch為"×",則從數值棧st中退棧兩個運算數,相乘後進棧數值棧st中
  • 字元ch為"÷",則從數值棧退棧兩個運算數,相除後進棧st中(若除數為0,則提示錯誤)
/*****************************************************************
*函式名:compvalue
*函式功能描述:字尾表示式postexp(也稱逆波蘭表示式)的值計算出來
*函式引數:postexp[]-存放字尾表示式的字元陣列
*函式返回值:返回值是計算出來的值
*作者:王賦睿
*函式建立日期:2018.5.27
*函式修改日期:尚未修改
*修改人:尚未修改
*修改原因:尚未修改
*版本:1.0
*歷史版本:無
*****************************************************************/
float compvalue(char postexp[])
{
	float d=0;
	char ch;
	int i = 0;
	ST st;
	
	st.top = -1;
	ch = postexp[i++];
	while (ch != NUL)
	{
		switch (ch)
		{
		case '+':   //從數值棧st退棧兩個運算數,相加後進入數值棧st中
			st.data[st.top - 1] += st.data[st.top];
			st.top--;
			break;
		case '-':   //從數值棧st退棧兩個運算數,相減後進入數值棧st中
			st.data[st.top - 1] -= st.data[st.top];
			st.top--;
			break;
		case '*':   //從數值棧st退棧兩個運算數,相乘後進入數值棧st中
			st.data[st.top - 1] *= st.data[st.top];
			st.top--;
			break;
		case '/':   //從數值棧st退棧兩個運算數,相除後進入數值棧st中
			if (st.data[st.top] != 0)
				st.data[st.top - 1] /= st.data[st.top];
			else
			{
				printf("\n\t除零錯誤!\n");
				exit(0);
			}
			st.top--; 
			break;
		default:
			d = 0;     //將數字字元轉換成對應的數值存放在d中				
			while (ch >= '0'&&ch <= '9')	//判定為數字字元
			{
				d = 10 * d + ch - '0';
				ch = postexp[i++];
			}
			st.top++;
			st.data[st.top] = d;
			break;
		}
		ch = postexp[i++];
	}

	return st.data[st.top];
}

  

  進行測試用例:

int main()
{
	char exp[MaxSize] = { "15*66-89*13+10" };
	char postexp[MaxSize];
	trans(exp, postexp);
	float d = compvalue(postexp);
	printf("%f", d);
	return 0;
}

本程式在VS2017下執行通過

相關文章