天天看點

第三次寒假作業1.0

GitHub develop分支

GitHub master

隊員:

蓋嘉軒031602211

許郁楊031602240

我和我的隊友計劃先寫一篇1.0,描述一下程式本身包括程式的代碼、編碼規範、送出記錄等等,由于要說的東西很多,例如合作過程和合作體會會在下一篇團隊總結中講述

  • 隊員分工
  • 日程規劃
  • 程式設計規範
  • 實作設計思路和遇到的困難
  • 代碼
  • 運作測試
  • 送出記錄
  • 合作證據

一開始我們決定要使用多源檔案編寫程式,由于我C++不是很熟練,而且這道題我一開始用C語言嘗試寫了一下,遇到了許多問題,是以我決定采取我隊友的方案,一開始是這樣分類的:

檔案名 功能
head.h 頭檔案
main.cpp 使用者界面
calculate.cpp 生成題目、檢驗結果
fraction.cpp 生成、計算真分數
stack.cpp 計算表達式結果

這個版本我負責head、main、calculate;因為根本不會用棧去處理四則運算,也不會寫分數的部分,是以隻能請隊友去寫fraction、stack,但由于和隊友的程式不相容,是以我在隊友的指導下又寫了一個1.0 的版本

1.0的檔案分類

初始界面
generate.cpp 生成題目
生成、計算分數
計算表達式
verify.cpp 檢驗答案并輸出

與最開始相比,就是将calculate分成generate、verify,以及對fraction、和stack做了一些改進。盡可能的将功能子產品化。

由于發作業時剛過完年,我和隊友各自都有事,而且我也需要一些時間去百度本次作業需要的知識,是以一直到8号才開始正式寫代碼。

由于13号隊友在測試程式時發現了一個大坑,隊友一夜未眠進行debug,才解決。。。。

一開始我們就決定要用描述性變量名,經過修改後:

變量名 作用
i,j 循環變量
flag 判斷變量
tmp 臨時變量
frac 分數
low、high 數字範圍
answer 結果
sign 符号
para 參數
infix 中綴表達式
postfix 字尾表達式
point 棧頂指針

函數的命名也采用的是描述性命名

函數
getRand 擷取随機數
getAndCalculate 擷取表達式
transEquation 中綴轉為字尾
countEquation 計算字尾的值
ifOnly 判斷是否重複
checkAndOutput 檢查答案并輸出正确答案
finalOut 輸出正誤個數
gcd 最大公約數
fixUp 保持分母為正
getFrac 生成分數
transFrac 整數化為分數
simplify 分數化簡
transString 分數轉為字元串(不判斷整數)
transToString 分數轉為字元串(判斷整數)
operator 分數四則運算

我負責程式的界面(中英文界面切換),生成題目,檢驗結果。生成界面還是比較容易,看完我的代碼基本都能明白我的思路,主要是這次首次使用了static靜态變量,是以我主要講生成題目和檢驗結果。

  1. 首先分析完這道題後有以下幾個難點:
  • 随機數的生成(想要學習的話可以看我隊友的部落格。如何在C++中産生随機數)
  • 四則運算符号的随機生成
  • 括号的生成

一開始說實話我根本沒有頭緒,我試着用C語言寫了一下,根本寫不出來!後來還是我的搭檔告訴我這道題需要用到的知識,給我推薦了一些部落格,經過學習,我才知道要運用srand()函數和rand()函數生成随機數,而srand()函數需要一個“種子”去初始化,我用的是srand((unsigned int)(time(NULL))的方法,利用系統時鐘,産生不同的随機數種子。

單純的rand()會傳回一個0至RAND_MAX之間的随機數值,而RAND_MAX的值與int位數有關,最小是32767。不過rand()是一次性的,因為系統預設的随機數種子為1,隻要随機數種子不變,其生成的随機數序列就不會改變。

其實,對于rand()的範圍,我們是可以進行人為設定的,隻需要在宏定義中定義一個random(int x)函數,就可以生成範圍為0至x的随機數值。當然,也可以定義為random(a,b),使其生成範圍為a至b的随機數值。

至于運算符号,我利用随機數的生成,采用switch結構,随機選擇+、-、x、/、可以在之後的代碼中看到。

最後是括号,首先分成兩種情況,有括号出現的算式,沒有括号出現的算式,而且還要讓算式中不能出現過多重複的數字,是以分成四種情況,一次生成一個A+B ,通過循環讓這種類型的式子進行随機組合。

  1. 檢驗結果

我寫程式時最頭疼的就是這個問題,因為根本沒有思路。後來在隊友的提示下在生成題目時就将題目存儲到不同的變量中ans和result,最後進行比較。

/*************************************************************
檔案名:head.h 
作者:蓋嘉軒 日期:2016/02/16
描述: 頭檔案 
*************************************************************/

#pragma once
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<sstream>
#include<cmath>
#include<ctime>
#include<stack>
#include<cassert>
#define Min(x,y) (((x)<(y))?(x):(y))
#define Max(x,y) (((x)>(y))?(x):(y))
#define random(a,b) (rand()%(b-a+1)+a)   
#define MAX 1000
using namespace std;

/*generate.cpp*/ 
int getRand(int down,int up);
void getAndCalculate(int opt,int num,int low,int high,char flag1,char flag2,char flag3);

/*Stack.cpp*/ 
void transEquation(string infix,char postfix[]);
string countEquation(string infix);

/*verify.cpp*/
int ifOnly(string str,string se[],int k);
void checkAndOutput(string equ,int n,int opt);
void finalOut(int opt);

/*fraction.cpp*/
class Fraction
{
	private:
		int numerator,denominator;
		string numerators,denominators;
		int greatestCommonDivisor(int x,int y);
		void fixUp(Fraction frac); 
	
	public:
		Fraction();
		Fraction(int numerator,int denominator);
		Fraction getFrac(int l,int h);
		Fraction transFrac(int up,int down);
		
		Fraction simplify(Fraction frac);
		string transString(Fraction frac);
		string transToString(Fraction frac);
		
		friend const Fraction operator +(Fraction frac1,Fraction frac2);  
        friend const Fraction operator -(Fraction frac1,Fraction frac2);  
        friend const Fraction operator *(Fraction frac1,Fraction frac2);  
        friend const Fraction operator /(Fraction frac1,Fraction frac2); 
};
           
/*************************************************************
檔案名:main.cpp
作者:蓋嘉軒 日期:2016/02/16
描述: 初始界面 
主要功能包括:語言切換、功能選擇 
*************************************************************/

#include"head.h"

int main()
{
	int i,j,k,num,low,high,opt;
	char flag1,flag2,flag3,tmp;
	
	cout<<"請問要選擇哪種語言/Which language you would like to choose?\n";
	cout<<"輸入 1 切換中文 ;輸入 2 切換英文./input 1 for Chinese ; input 2 for English: ";
	cin>>opt;
	 
	if(opt==1)
	{
		cout<<"請輸入題目數(1~1000):";
		cin>>num;
		
		cout<<"請輸入算式中的數字大小的絕對值範圍(如:1 100):";
		cin>>low>>high;
		
		cout<<"是否允許乘除(y/n):";
		cin>>flag1;
		
		cout<<"是否允許分數(y/n)(結果請以假分數的形式輸出,例如13\\5):";
		cin>>flag2;
		
		cout<<"是否允許括号(y/n):";
		cin>>flag3;
		
		cout<<"********************************************************************"<<endl;
		cout<<"                                                                    "<<endl;
	}
    if(opt==2)
	{
		cout<<"Please enter a number as the amount of the calculation questions: ";
		cin>>num;
		
		cout<<"Please enter the size range of the numbers'absolute value in the equation.(e.g 1 100): ";
		cin>>low>>high;
		
		cout<<"Would you permit the  multiplication and division as a part of the equation?(y/n): ";
		cin>>flag1;
		
		cout<<"Would you permit the fraction as a part of the equation?(y/n): ";
		cin>>flag2;
		
		cout<<"Would you permit the parenthesis--() as a part of the equation?(y/n): ";
		cin>>flag3;
		
		cout<<"********************************************************************"<<endl;
		cout<<"                                                                    "<<endl;
	}
	getAndCalculate(opt,num,low,high,flag1,flag2,flag3);
	return 0;
}
           
/*************************************************************
檔案名:generate.cpp
作者:蓋嘉軒 日期:2016/02/16
描述: 生成表達式 
主要功能包括:生成随機數、生成表達式 
*************************************************************/

#include"head.h"

int flag=1,k=0;
int getRand(int down,int up)//生成随機數 
{
	if (flag==1)
	{
		flag=0;
		srand((unsigned)time(NULL));//種子 
    }
    return random(down,up);
}
string equation[MAX];
void getAndCalculate(int opt,int num,int low,int high,char flag1,char flag2,char flag3)
{
	int i=1,tmp;
	char sign;
	while (i<=num)
	{
		int flag4=1,number=getRand(2,7);
		string paras1,paras2,equ;
		for (int j=0;j<number;j++)
		{
			if (flag1=='y') //允許乘除 
			{
				tmp=getRand(1,4);
				switch (tmp)
				{
					case 1:sign='+';
					       break;
					case 2:sign='-';
					       break;
					case 3:sign='*';
					       break;
					case 4:sign='/';
					       break;
				}
			}
			else
			{
				tmp=getRand(1,2);
				switch (tmp)
				{
					case 1:sign='+';
					       break;
					case 2:sign='-';
					       break;
				}
			}
			if (flag2=='y') //允許分數 
			{
				tmp=getRand(1,3);
				switch (tmp)
				{
					case 1: //整數和整數 
					{
    				    stringstream tmps1,tmps2;
     	    			tmps1<<getRand(low,high);
	        			tmps1>>paras1;
		        		tmps2<<getRand(low,high);
			        	tmps2>>paras2;
		  				break;
				    }
					case 2: //整數和真分數 
					{
						stringstream tmps;
						tmps<<getRand(low,high);
						tmps>>paras1;
						Fraction frac2=frac2.simplify(frac2.getFrac(low,high));
						paras2=frac2.transString(frac2);
						break;
					}
					case 3: //分數和分數 
					{
						Fraction frac1=frac1.simplify(frac1.getFrac(low,high));
						Fraction frac2=frac2.simplify(frac2.getFrac(low,high));
						paras1=frac1.transString(frac1);
						paras2=frac2.transString(frac2);
						break;
					}
				}
			}
			else
			{
				stringstream tmps3,tmps4;
				tmps3<<getRand(low,high);
				tmps3>>paras1;
				tmps4<<getRand(low,high);
				tmps4>>paras2;
			}
			if (flag3=='y') //允許括号
			{
				tmp=getRand(1,4);
				switch (tmp)
				{
					case 1: //無括号 
					{
						if (flag4==1)
						{
							equ=paras1+sign+paras2;
							flag4=0;
						}
						else equ=equ+sign+paras1;
					    break;
					}
					case 2:
					{
						if (flag4==1)
						{
							equ=paras2+sign+paras1;
							flag4=0;
						}
						else equ=paras1+sign+equ;
					    break;
					}
					case 3: //有括号 
					{
						if (flag4==1)
						{
							equ="["+paras1+sign+paras2+"]";
							flag4=0;
						}
						else equ="["+equ+sign+paras1+"]";
					    break;
					}
					case 4:
					{
						if (flag4==1)
						{
							equ="["+paras2+sign+paras1+"]";
							flag4=0;
						}
						else equ="["+equ+sign+paras1+"]";
						break;
					}
				}
			}
			else
			{
				tmp=getRand(1,2);
				switch (tmp)
				{
					case 1:
					{
						if (flag4==1)
						{
							equ=paras1+sign+paras2;
							flag4=0;
						}
						else equ=equ+sign+paras1;
					    break;
					}
					case 2:
					{
						if (flag4==1)
						{
							equ=paras2+sign+paras1;
							flag4=0;
						}
						else equ=paras1+sign+equ;
					}
				}
			}
		}
		if (ifOnly(equ,equation,k)==1) //判斷表達式是否重複 
		{
			k++;
			equation[k]=equ;
            checkAndOutput(equ,i,opt);
			i++;
		}
	}
    finalOut(opt);
}
           
/*************************************************************
檔案名:verify.cpp
作者:蓋嘉軒 日期:2016/02/16
描述: 檢驗和輸出結果 
主要功能包括:判重、檢驗答案、輸出答案 
*************************************************************/

#include"head.h"

int correct=0,wrong=0;
int ifOnly(string str,string se[],int k) //判斷表達式是否重複 
{
	int count=0;
	for (int i=0;i<k;i++)
	{
		if (str!=se[i]) count++;
		else break;
	}
	    if (count==k) return 1;
		else return 0;
}
void checkAndOutput(string equ,int n,int opt) //檢驗答案并輸出正确答案 
{
	string result=countEquation(equ),ans;
	cout<<"("<<n<<") "<<equ<<"=";
	cin>>ans;
	if (ans==result)
	{
		if(opt==2)cout<<"Correct Answer!"<<endl;
		else cout<<"正确"<<endl;
		correct++;
	}
	else
	{
		if(opt==2)cout<<"Wrong Answer! The correct answer is "<<result<<endl;
		else cout<<"錯誤,正确答案為:"<<result<<endl;
		wrong++;
	}
}
void finalOut(int opt) //輸出正誤個數 
{
	cout<<"********************************************************************"<<endl;
	cout<<"                                                                    "<<endl;
	if(opt==2) cout<<"  "<<correct<<" answers are correct, "<<wrong<<" answers are wrong.";
	else cout<<"做對了"<<correct<<"道題,做錯了"<<wrong<<"道題";	
}
           

由于我們使用生成随機數的方法生成運算符号,并且将各種功能子產品化設計,是以我們的程式可以自行選擇計算的難度,是否出現乘除,括号分數;而且不隻有四個數字,可能出現很多數字在同一個算式中,不僅國小生可以用,國中生,高中生也可以使用!.

第三次寒假作業1.0
第三次寒假作業1.0

由于我主要是将代碼先發給隊友,在與他的代碼相容後才能在github上釋出,是以送出記錄不是很多

第三次寒假作業1.0
第三次寒假作業1.0

這是我在最開始寫好的三個檔案,用QQ給隊友發了過去

第三次寒假作業1.0