C++實現計算器功能(包括計算含未知量的式子),輸出後綴表達式

大概描述

       用c++語言在vc中實現部分數學計算功能。其中實現的數學計算功能包括加減乘除運算、開方計算、自然對數運算、以10爲底的對數運算、冪計算、正弦餘弦計算。

       由用戶輸入要計算的表達式,然後判斷表達式是否含有未知變量,若含有未知變量則調用可以處理未知量的計算函數,否則調用一般的計算函數。

       把用戶輸入的表達式存進一個存放中綴表達式的字符數組,再定義一個存放後綴表達式的字符數組,通過調用中綴轉後綴的函數,將轉爲後綴的字符存進後綴表達式的字符數組。

       用存放後綴表達式的字符數組調用計算函數,遇到數字時,調用讀取數據的函數,把數字字符轉換爲double型的數。最後將答案輸出。

      爲方便,對於未知量的處理,人爲地規定了未知量的順序和名字,規定如下:未知量的定義最多有5個,其名字和順序如下x、y、z、m、n。

      同時,爲方便,常見函數的名字一律採用一個字母代替,其中sin函數用s代替,cos函數用c代替,ln函數用l代替,log10函數用g代替。

實驗目的

1   用c++語言在vc中實現部分數學計算功能。其中實現的數學計算功能包括加減乘除運算、開方計算、自然對數       運算、以10爲底的對數運算、冪計算、正弦餘弦計算。

2   通過用戶輸入表達式,實現字符串的處理:實現中綴表達式轉化後綴表達式,實現後綴表達式的數字字符的處       理。

3   實現後綴表達式的計算。

實驗設計

1、由用戶輸入中綴表達式,把表達式存進一個字符數組,再定義一個字符數組用於存放後綴表達式

2、寫一個將中綴表達式轉爲後綴表達式的函數,利用棧來處理操作符和括號

3、寫一個從後綴表達式獲取數據的函數

4、寫一個函數,判斷用戶輸入的表達式是否含有未知量

5、寫兩個計算函數,一個用於計算不含未知量的表達式,一個用於計算含有未知量的表達式

6、規定未知量的名字和順序(本程序只定義了5個未知量,其名字依次爲x、y、z、m、n)

7、調用<cmath>中的常見函數;如調用其正弦餘弦函數對數函數等

實驗描述

  1. 在主函數測試計算器,提示用戶相應的函數名字縮寫定義,提示用戶輸入表達式,並且創建兩個字符數組,分別用於存放中綴  表達式和後綴表達式,再用這兩個數組去調用中綴轉後綴的函數,繼而調用後綴表達式的計算函數,具體看main()函數。
  2. 中綴表達式轉後綴表達式函數:創建一個字符型的棧,用於存放操作符。將存放中綴表達式的字符數組作爲實參來調用此函數。然後從i=0開始讀取字符數組裏的元素,若遇到數字或者未知量,則調用讀取數據的函數;若遇到函數縮寫名時,則先將後面的數存進後綴表達式的數組,再將函數縮寫名存進後綴表達式的數組;若遇到‘(’,則將其進棧;若遇到‘)’,則將棧頂的操作符存進後綴表達式的字符數組,並且出棧,直到遇到‘(’,將‘(’出棧;若遇到二目操作符,則比較此操作符和棧頂操作符的優先級,若棧頂操作符的優先級高,則將棧頂操作符放進後綴表達式,並將其出棧,一直進行此操作,直到棧裏的操作符優先級比此操作符的優先級低。具體看getpostfix()函數。
  3. 獲取操作符優先級的函數:加減的優先級定義爲1,乘除的優先級定義爲2,冪計算的優先級定義爲3。具體看getpri()函數。
  4. 判斷字符是否是二目操作符函數,具體看is_opr()函數。
  5. 判斷字符是否是函數縮寫名,具體看is_char()函數。
  6. 判斷字符是否是數字或者未知量函數,具體看is_num()函數。
  7. 從後綴表達式讀取數據函數:在讀取後綴表達式的字符時,遇到數字,則調用此函數。調用此函數時,除了要傳後綴表達式的數組做參數外,還要傳此時的i的引用來做參數。具體看read_number()函數。
  8. 判斷表達式是否含有未知量函數,具體看is_xyzmn()函數。
  9. 計算不含未知量表達式的函數:創建一個double型的數組,用於存放後綴表達式裏的數據和進行計算後的中間結果。遍歷存放後綴表達式的字符數組,若遇到數字,則調用讀取數據的函數,並且把讀取到的數據放在double型的數組中;若遇到操作符或者函數縮寫名,則進行相應計算。具體看getpanswer()函數。
  10. 計算含有未知量表達式的函數: 計算含有未知量表達式的函數實現和計算不含未知量表達式的函數實現差不多。只不過在遍歷後綴表達式數組時,若遇到未知量,則將未知量的值直接存進double型數據數組,而不存未知量。具體看geteanswer()函數。

完整代碼

/*************************************************************************
    > File Name: mycomputer.c
    > Author: surecheun
    > Mail: [email protected]
    > Created Time: 2017年4月18日
 ************************************************************************/

#include<iostream>
#include<stack>
#include<string>
#include<vector>
#include <sstream>
#include<cmath>
#define max 100

using namespace std;

//對二目操作符進行優先級的量化轉變
int getpri(char c) {
	switch (c) {
	case '+':return 1; break;
	case '-':return 1; break;//‘+’和‘-’的優先級爲最低級1
	case '*':return 2; break;
	case '/':return 2; break;//‘*’‘/’的優先級爲中級2
	case'^':return 3; break;//‘^’的優先級最高,爲3
	default:return 0; break;
	}
}
//判斷字符是否是二目操作符
int is_opr(char c)
{
	switch (c) {
	case '+':
	case '-':
	case '*':
	case '/':
	case'^':return 1; break;
	default:return 0; break;
	}
}
//判斷字符是否是數字或者是未知量
int is_num(char c)
{
	switch (c)
	{
	case'0':
	case'1':
	case'2':
	case'3':
	case'4':
	case'5':
	case'6':
	case'7':
	case'8':
	case'9':
	case'.':
	case'x':
	case'y':
	case'z':
	case'm':
	case'n':
		return 1; break;
	default:return 0; break;
	}
}
//判斷字符是否代表特殊函數,爲了程序簡單,取特殊函數的某個字母代替該函數(此類函數均爲單目運算)
int is_char(char c)
{
	switch (c)
	{
	case's'://sin函數
	case'c'://cos函數
	case'q'://開方函數
	case'l'://ln函數
	case'g'://log10函數
		return 1; break;
	default:return 0; break;
	}
}

//中綴表達式改寫成後綴表達式
void getpostfix(char *in, char *post)
{
	stack<char> st;//創建一個字符類的棧,用於存放操作符
	st.push('@');//村一個@進棧底,方便於最後判斷棧裏的操作符是否已經全部出棧
	int i = 0;
	int j = 0;
	while (in[i] != '#')//‘#’爲字符數組的最後一個元素
	{
		char ch = in[i];
		if (is_num(ch))//判斷是否是數字後者未知量,若是則把其放進後綴字符數組
		{
			post[j++] = ch;
			i++;
		}
		else if (is_char(ch))//判斷字符是否是常見(單目運算)函數的縮寫,若是則將函數後面的數放進後綴字符數組,再將函數名縮寫放進後綴數組
		{
			i++;
			char ch1 = in[i];
			while (is_num(ch1) == 1)
			{
				post[j++] = ch1;
				i++;
				ch1 = in[i];
			}//把函數後面的數輸進後綴字符數組
			post[j++] = ' ';//用空格分開數和函數名
			post[j++] = ch;	//將函數名的縮寫放進後綴表達式的數組	 
		}
		else   //若字符是二目操作符或者是括號,則做以下操作
		{
			post[j++] = ' ';//用空格隔開操作符前後的數,便於後續讀取
			if (ch == '(')//如果字符是‘(’,則將字符進棧
			{
				st.push(ch);

			}
			else if (ch == ')')  //若字符是‘)’,則將棧中的字符輸進後綴表達式的字符數組,直至遇到‘(’ 
			{
				ch = st.top();
				while (ch != '(')
				{
					post[j++] = ch;
					st.pop();
					ch = st.top();
				}
				st.pop(); //將‘(’出棧

			}
			else//若字符是二目操作符‘則做以下操作
			{
				int thisPri = getpri(ch);  //獲取操作符的優先級
				char prevOpt = st.top();  //獲取棧頂操作符
				int prevPri = getpri(prevOpt); //獲取棧頂操作符的優先級
				while (thisPri <= prevPri)//比較兩者的優先級,若棧頂操作符的優先級高,則將棧頂操作符放進後綴表達式,並將其出棧 
				{
					post[j++] = prevOpt;//將棧頂操作符放進後綴表達式
					st.pop();  //將其出棧
					prevOpt = st.top();//取棧中新棧頂的操作符
					prevPri = getpri(prevOpt);//同理獲取棧中新棧頂的操作符的優先級
				}
				st.push(ch);  //把新的操作符進棧
			}
			i++;
		}
	}

	char ch = st.top();
	while (ch != '@')
	{
		post[j++] = ch;
		st.pop();
		ch = st.top();
	}//當棧底元素不是’@‘時,將棧頂元素放進後綴表達時的字符數組
	post[j] = '\0';
}
//讀取數據,將後綴表達式的字符數字轉變爲數
double read_number(char *dest, int *i)
{
	double x = 0;
	int num = 0;
	int j;
	while (dest[*i]<'0' || dest[*i]>'9') (*i)++;
	while (dest[*i] >= '0'&&dest[*i] <= '9')
	{
		x = x * 10 + (dest[*i] - '0');
		(*i)++;
	}

	if (dest[*i] == '.')//遇到小數點,則計算後面的數字個數,用於判斷要把數字除以多少個10
	{
		(*i)++;
		while (dest[*i] >= '0'&&dest[*i] <= '9')
		{
			num++;
			x = x * 10 + (dest[*i] - '0');
			(*i)++;
		}
	}
	for (j = 0; j < num; j++)
		x = x / 10;
	return x;
}

//計算沒有未知數的表達式
double getpanswer(char *dest)
{
	double x[50];//創建一個數組,用於存放數據
	int len = 0;
	int i = 0;
	while (dest[i] != '\0')//當字符數組中的字符不是’/0‘時
	{
		if (dest[i] >= '0'&&dest[i] <= '9')//遇到數字時
			x[len++] = read_number(dest, &i);//調用讀取數據函數,並且把i的地址傳過去,這樣i的值將會在函數調用後,也改變,從而使i跳到下一個類型的字符
		else if (is_char(dest[i]) || is_opr(dest[i]))//假設字符是二目操作符,或者是函數名縮寫,則進行計算
		{
			switch (dest[i])
			{

			case '+'://加法
				x[len - 2] = x[len - 2] + x[len - 1];
				len--;
				i++;
				break;
			case '-'://加法
				x[len - 2] = x[len - 2] - x[len - 1];
				len--;
				i++;
				break;
			case '*'://乘法
				x[len - 2] = x[len - 2] * x[len - 1];
				len--;
				i++;
				break;
			case '/'://除法
				x[len - 2] = x[len - 2] / x[len - 1];
				len--;
				i++;
				break;
			case'^'://冪運算
				for (double i = 1; i < x[len - 1]; i++)
				{
					double y = x[len - 2];
					x[len - 2] = x[len - 2] * y;
				}
				len--;
				i++;
				break;
			case's'://sin函數
				x[len - 1] = sin(x[len - 1]);
				i++;
				break;
			case'c'://cos函數
				x[len - 1] = cos(x[len - 1]);
				i++;
				break;
			case'q'://開方函數
				x[len - 1] = sqrt(x[len - 1]);
				i++;
				break;
			case'l'://ln函數
				x[len - 1] = log(x[len - 1]);
				i++;
				break;
			case'g'://log10函數
				x[len - 1] = log10(x[len - 1]);
				i++;
				break;
			}

		}
		else i++;
	}
	return x[len - 1];

}
//計算含有未知量的表達式
double geteanswer(char *dest, double x1, double y1, double z1, double m1, double n1)
{
	double x[50];
	int len = 0;
	int i = 0;
	while (dest[i] != '\0')
	{
		if (dest[i] >= '0'&&dest[i] <= '9')
			x[len++] = read_number(dest, &i);
		else if (dest[i] == 'x' || dest[i] == 'y' || dest[i] == 'z' || dest[i] == 'm' || dest[i] == 'n')//如果遇到未知量,則將未知量的值直接存進數據數組,而不存未知量
		{
			if (dest[i] == 'x')
				x[len++] = x1;
			else if (dest[i] == 'y')
				x[len++] = y1;
			else if (dest[i] == 'z')
				x[len++] = z1;
			else if (dest[i] == 'm')
				x[len++] = m1;
			else
				x[len++] = n1;
			i++;
		}
		else if (is_char(dest[i]) || is_opr(dest[i]))//遇到操作符或者函數名字縮寫,則同不含有未知量的計算一樣
		{
			switch (dest[i])
			{

			case '+':
				x[len - 2] = x[len - 2] + x[len - 1];
				len--;
				i++;
				break;
			case '-':
				x[len - 2] = x[len - 2] - x[len - 1];
				len--;
				i++;
				break;
			case '*':
				x[len - 2] = x[len - 2] * x[len - 1];
				len--;
				i++;
				break;
			case '/':
				x[len - 2] = x[len - 2] / x[len - 1];
				len--;
				i++;
				break;
			case'^':
				for (double i = 1; i < x[len - 1]; i++)
				{
					double y = x[len - 2];
					x[len - 2] = x[len - 2] * y;
				}
				len--;
				i++;
				break;
			case's':
				x[len - 1] = sin(x[len - 1]);
				i++;
				break;
			case'c':
				x[len - 1] = cos(x[len - 1]);
				i++;
				break;
			case'q':
				x[len - 1] = sqrt(x[len - 1]);
				i++;
				break;
			case'l':
				x[len - 1] = log(x[len - 1]);
				i++;
				break;
			case'g':
				x[len - 1] = log10(x[len - 1]);
				i++;
				break;
			}

		}
		else i++;
	}
	return x[len - 1];
}

int is_xyzmn(char *d, int j)//判斷表達式是否含有未知量
{
	int p;
	for (int o = 0; o < j; o++)
	{
		if (d[o] == 'x')
		{
			p = 1;
			break;
		}
		else
			p = 0;
	}
	return p;
}


int main() {
	cout << "sin請用s代替;cos請用c代替;sqrt請用q代替;ln用l代替;log10用g代替!" << endl;
	cout << "如果表達式含有未知數,請按下面的順序定義變量名字:x,y,z,m,n.(最多隻允許5個變量)" << endl;
	char infix[max];//創建一個字符數組,用於存放中綴表達式
	char postfix[max];//創建一個字符數組,用於存放後綴表達式
	int i = 0;
	char ch;
	while ((ch = getchar()) != '\n')
	{
		infix[i++] = ch;//把輸入的字符放進中綴表達式的數組
	}
	infix[i++] = '#';//放進一個標誌字符,便於判斷中綴表達式是否已查完
	infix[i] = '\0';
	getpostfix(infix, postfix);//調用中綴轉後綴函數
	cout << "輸出後綴表達式:" << postfix << endl;
	double a;//用於存放答案

	if (is_xyzmn(infix, i))//判斷表達式是否含有未知量,若含有,則輸入未知量的取值,並且調用含有未知量表達式的計算函數
	{
		cout << "請按順序輸入未知數的取值,若未知數不夠5個,請用0補齊:";
		double a1;
		double a2;
		double a3;
		double a4;
		double a5;
		cin >> a1;
		cin >> a2;
		cin >> a3;
		cin >> a4;
		cin >> a5;
		a = geteanswer(postfix, a1, a2, a3, a4, a5);//調用含有爲質量的計算函數
	}
	if (!is_xyzmn(infix, i))//若表達式沒有含未知量,則調用不含未知量的計算函數
		a = getpanswer(postfix);//調用不含爲質量的計算函數
	cout << a << endl;//輸出答案
	getchar();
	return 0;
}
歡迎指教!















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章