C++ は電卓を実装します

1. 実験目的(対象)

1. スタックの操作と応用をマスターします。

2. アルゴリズムの堅牢性を理解する。

2. 実験内容(コンテンツ)

1. 電卓クラスで lp、rp、operate 関数を実現します。

2. 計算機クラスの評価関数を改善し、すべての不正な入力をフィルタリングして除外し、左括弧と右括弧が一致しない入力を処理するなど、入力の正当性チェックを強化します。

3. 電卓をテストするアプリケーションを作成します。

4. 次の式を使用してテストします。

(56-23)/8-4# 期望结果:0.125 
34+p(u89-12.3)k/3# 期望结果:59.5667 
89.5*749+25)# 期望结果:输入有误 
(8*(7-4)# 期望结果:输入有误 
65*(72+98)(70-45)# 期望结果:输入有误 
6*# 期望结果:输入有误 
)5+3(# 期望结果:输入有误

5. 計算結果を確認します。

3. 実験手順 (Yourstepsorcodes)

1st.全体デザイン

この実験の全体設計は、主な枠組みとして 2 つのクラス (クラス) に基づいています。1 つは Stack クラス、もう 1 つは Calculator クラスです。このうち、StackクラスにはGenericが使用されます。全体的なデザインは次のとおりです。

/********************************************栈的构建*********************************************/ 
template<typenameT> 
classStack{
    
    //栈模板类的创建与其数据变量 
private:
	T* n;//栈中存储数据的数组; 
	Stac_Int Nlen;//栈当前长度; 
	Stac_Int capacity;//栈的最长长度; 
	Stac_Int top_;//存储当前栈顶元素的下标; 
public:
	Stack();//构造函数 
	~Stack();//析构函数 
	void push(Tx);//栈的 push函数,向栈顶添加元素 
	T top();//栈的 top函数,返回栈顶元素 
	bool isEmpty();//栈的为空判断 
	bool isFull();//栈的为满判断 
	Stac_Int backNlen();//返回栈的长度 
	void pop();//栈的 pop函数,弹出栈顶元素 
	void to_empty();//清空栈 
	};
	//****************************************计算器类的构建*******************************************// 
classCalculator{
    
     
private:
	Stack<char> optr;//运算符栈; 
	Stack<double> opnd;//运算数栈; 
	char* at;//计算器类的运算式字符串的头指针; 
	int n; 
	int lp(chara);//返回字符栈头部字符的优先级命令; 
	int rp(chara);//返回录入读取字符的优先级命令; 
	double char_to_double(char*a,intn);
	//该函数进行字符串的小数转换成 double类型的小数(但只对有一个点 的小数有作用) 
	double operate_double(chara,doublex1,doublex2);//该函数计算两数的四则运算 
public:
	Calculator();//无参构造函数; 
	Calculator(char*a,intnn);//含参构造函数; 
	~Calculator();//析构函数; 
	void operate();对输入的一串算术字符串式的处理(核心处理函数); 
	bool rr(chara)const;//辅助函数; 
	bool evaluate()const;//判断输入的一串算术字符串是否合法; 
	double result();//返回运算结果; 
	};

StackクラスとCalculatorクラスのコア関数定義については、アルゴリズムフレームワークの欄で詳しく説明します。

2. アルゴリズムフレームワーク

1. Stack クラスのコア機能のアルゴリズム フレームワークについて:

中心となる機能は次のとおりです。

void push(T x);//栈的 push函数,向栈顶添加元素 
T top();//栈的 top函数,返回栈顶元素 
void pop();//栈的 pop函数,弹出栈顶元素 
void to_empty();//清空栈

1>. プッシュ機能について:

//栈的 push函数,向栈顶添加元素 
template<typename T> 
void Stack<T>::push(T x){
    
    
 if(!isFull()){
    
     
 	top_++; 
 	n[top_]=x; 
 	Nlen++; 
 	}
 else
 	printf("堆栈已满;创建失败!\n"); 
 }

スタックが空ではない場合、スタックの最上位要素のフット コードを保存する top_ を最初に 1 つ増やし、次に n[top_]=x; このとき、スタック要素は Nlen++; と増加します。スタックが空の場合、「スタックがいっぱいです。作成に失敗しました!」と出力されます。

2>. ポップ機能について:

//栈的 pop函数,弹出栈顶元素 
template<typename T> 
void Stack<T>::pop(){
    
     
	top_--; 
	Nlen--; 
}

top_–;Nlen–; のみが必要です。

3>. トップ関数について:

//栈的 top函数,返回栈顶元素 
template<typename T> 
T Stack<T>::top(){
    
     
	returnn[top_]; 
}

先頭の要素を直接返すだけです。

4>. to_empty 関数について:

//清空栈 
template<typename T> 
void Stack<T>::to_empty(){
    
     
	top_=0; 
	Nlen=0; 
}

スタックの最上位要素のフッターを 0、スタック長を 0 にするだけです。

2. Calculator クラスのコア関数のフレームワークについて:

中心となる機能は次のとおりです。

void operate();对输入的一串算术字符串式的处理(核心处理函数);
double char_to_double(char*a,int n);
//该函数进行字符串的小数转换成 double类型的小数(但只对有一个点的小 数有作用)

以下はヘルパー関数です。

 int lp(char a);//返回字符栈头部字符的优先级命令; 
 int rp(char a);//返回录入读取字符的优先级命令; 
 double operate_double(char a,double x1,double x2);//该函数计算两数的四则运算 
 bool rr(char a)const;//辅助函数; 
 bool evaluate()const;//判断输入的一串算术字符串是否合法; 
 double result();//返回运算结果;

主な定義については、ソース コードを参照してください。

1>. char_to_double 関数の実装アルゴリズムについて説明します。

//该函数进行字符串的小数转换成 double类型的小数(但只对有一个点的小数有作用) 
double Calculator::char_to_double(char*a,int n){
    
     
	double su=0; 
	int i; 
	int t1=-1; 
	int t2; 
	for(i=0;i<n;i++){
    
     
		if(a[i]=='.'){
    
     
			t1=i; break; 
			} 
		}
		if(t1!=-1){
    
     
			t2=t1-1; 
			for(i=0;i<t1;i++,t2--){
    
     
				su+=(a[i]-'0')*pow(10,t2); 
				}
			int j=-1; 
			for(i=t1+1;i<n;i++,j--){
    
     
				su+=(a[i]-'0')*pow(10,j); 
				} 
			}
			else {
    
     
				t2=n-1; 
				for(i=0;i<n;i++,t2--) 
					su+=(a[i]-'0')*pow(10,t2); 
					}
				return su; 
		}

この関数では、su の double 型変数を使用して、最終的な文字列型の数値を double 型に変換した後、10 進数を格納します。まず文字列の数値を渡し、最初に小数点があるかどうかを判断します。

for(i=0;i<n;i++){
    
     
	if(a[i]=='.'){
    
     
		t1=i; 
		break; 
		} 
	}

その場合、t1 を使用して現在の配列要素の添え字を格納します。t1 の初期値は -1 で、文字列数値に小数点があるかどうかを判断します。存在する場合、整数部分と小数部分の 2 つの部分に分割して処理されます。

if(t1!=-1){
    
    
	t2=t1-1; 
	for(i=0;i<t1;i++,t2--){
    
     
		su+=(a[i]-'0')*pow(10,t2);
	}
	int j=-1; 
	for(i=t1+1;i<n;i++,j--){
    
     
		su+=(a[i]-'0')*pow(10,j); 
		} 
	}else {
    
     
		t2=n-1; 
		for(i=0;i<n;i++,t2--) 
			su+=(a[i]-'0')*pow(10,t2); 
}

そうでない場合は、整数部分だけを処理します。最後にsuに戻ります。
アルゴリズムの複雑さの分析: 最悪のケースは小数点がない場合で、その場合は各要素を 2 回処理する必要があるため、2*n 回処理されます。つまり O(n) です。

2>. 演算機能の実装について;

//对输入的一串算术字符串式的处理 
void Calculator::operate(){
    
     
	int i; 
	int k=0; 
	int t=0; 
	char a1; 
	int ii; 
	double tt1,tt2; 
	optr.push('#'); 
	for(i=0;i<n;i++){
    
     
		if(at[i]>='0'&&at[i]<='9'){
    
     
		/*这部分主要是在截取输入的算式字符串中的数字字符串部分, 截取之后需要借助 char_to_double()函数把字符串数字转换成 double类型的值,然后才可以压入 opnd栈中*/ 
		k=i; 
		while((at[i]>='0'&&at[i]<='9')||at[i]=='.'){
    
     
			t++; 
			i++; 
			}
		char u[100]; 
		for(ii=0;ii<t;ii++,k++) 
			u[ii]=at[k]; 
		opnd.push(char_to_double(u,t)); 
		t=0; 
		i--; 
		}
		else if(at[i]=='#'&&optr.top()=='#') /*如果此时读取到'#',则什么都不做, 也意味着结束整体处理*/		else{
    
    /*以下是算符优先级的实现*/ 
			while(1){
    
     
				if(rp(at[i])==-1) /*过滤非算符非数字的字符*/ 
				break; 
				else if(rp(at[i])>lp(optr.top())){
    
     optr.push(at[i]); /*如果传入字符优先级大于 optr栈顶字符优先级 则压入栈顶。*/ 
					break;
	}
	else if(rp(at[i])<lp(optr.top())){
    
     
		a1=optr.top(); 
		optr.pop(); 
		tt1=opnd.top(); 
		opnd.pop(); 
		tt2=opnd.top(); 
		opnd.pop(); 
		opnd.push(operate_double(a1,tt2,tt1)); /*如果传入字符优先级小于 optr栈顶字符优先级 则进行四则运算处理,处理结果,之后压入 opnd栈顶*/ 
		}
	else if(
		rp(at[i])==lp(optr.top())){
    
     
			optr.pop(); /*如果传入字符优先级与 optr栈顶优先级相等 则弹出 optr栈顶字符。*/ 
			break; } } } } }

この部分の主なアルゴリズムは、文字優先アルゴリズムと文字列番号をインターセプトするアルゴリズムです。次のように: まず、演算子の優先順位のアルゴリズムのアイデアを決定します。演算子の優先順位のアイデアは、上の図に示すように指定できます
ここに画像の説明を挿入

まず、水平方向の演算子を t2 演算子、垂直方向の演算子を t1 演算子と呼ぶことができます。上図はオペレータ間の優先関係を示しています。
優先順位の関係は、次の 4 つの算術規則から導出されます。
- 最初に乗算と除算、次に加算と減算
- 最初に左、次に右
- 最初に括弧の内側、次に括弧の外側 以下は、
演算子の演算順序のアルゴリズムの説明です。
※演算子スタック×1、オペランドスタック×2を設定します。ターミネータ # を x1 スタックの一番下に置きます。
*式を左から右にスキャンし、オペランドの場合は x2 スタックにプッシュし、スキャンを続行します。演算子 t2 の場合は、x1 スタックの最上位要素 t1 と比較します。 - t2 の
場合t1'#'、操作は終了し、結果は x2 スタックの先頭にあります。
- t2 の優先順位が高い場合、t2 は x1 スタックに入り、スキャンを継続します。
- 優先順位が等しい場合、先頭要素を終了します。 x1 スタック '('、スキャンを続行します。
- t2 の優先順位が低い場合は、t1 を x1 スタックから終了し、2 つのオペランド b と a を x2 スタックから終了し、操作の実行後に結果を x2 スタックにプッシュします。 at1b.その実装は次のとおりです

if(at[i]=='#'&&optr.top()=='#') /*如果此时读取到'#',则什么都不做, 也意味着结束整体处理*/ ; 
	else{
    
    /*以下是算符优先级的实现*/ 
		while(1){
    
     
			if(rp(at[i])==-1) /*过滤非算符非数字的字符*/ 
				break; 
			else if(rp(at[i])>lp(optr.top())){
    
     
				optr.push(at[i]); /*如果传入字符优先级大于 optr栈顶字符优先级 则压入栈顶。*/ 
				break; 
			}
			else if(rp(at[i])<lp(optr.top())){
    
     
				a1=optr.top(); 
				optr.pop(); 
				tt1=opnd.top(); 
				opnd.pop(); 
				tt2=opnd.top(); 
				opnd.pop(); 
				opnd.push(operate_double(a1,tt2,tt1)); /*如果传入字符优先级小于 optr栈顶字符优先级 则进行四则运算处理,处理结果,之后压入 opnd栈顶*/ 
				}
			else if(rp(at[i])==lp(optr.top())){
    
     
				optr.pop(); /*如果传入字符优先级与 optr栈顶优先级相等 则弹出 optr栈顶字符。*/ 
				break; 
				} 
	}

次に、文字列数値をインターセプトし、それを double 型の数値に変換して、opnd スタックの先頭にプッシュするアルゴリズムがあります。

if(at[i]>='0'&&at[i]<='9'){
    
     /*这部分主要是在截取输入的算式字符串中的数字字符串部分, 截取之后需要借助 char_to_double()函数把字符串数字转换成 double类型的值,然后才可以压入 opnd栈中*/ 
	k=i; 
	while((at[i]>='0'&&at[i]<='9')||at[i]=='.'){
    
     
		t++; 
		i++; 
		}
	char u[100]; 
	for(ii=0;ii<t;ii++,k++) 
		u[ii]=at[k]; 
		opnd.push(char_to_double(u,t)); 
		t=0; 
		i--; 
	}

まず k を使用して、元は数値である文字の添字を配列に格納し、次に特定の at[i] が数値または '.' でなくなるまで走査を続けます。t を使用して文字列の桁の長さを計算します。次に、それを u[100] の新しい配列に保存します。最後に、char_to_double() 関数を呼び出して u[t] を double 型に変換し、四則演算を直接実行できる数値を opnd スタックの先頭にプッシュします。最後に t を初期化します。次回の使用に便利です。

4. ソースプログラム(SourceProgram)

さまざまな理由により、ここでソース コードを表示するのは不便です。
ソースコードが必要な場合は、私にプライベートメッセージを送ってください。

おすすめ

転載: blog.csdn.net/weixin_42529594/article/details/120455067