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