第三次寒假作业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的文件分类

文件名 功能
head.h 头文件
main.cpp 初始界面
generate.cpp 生成题目
fraction.cpp 生成、计算分数
stack.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,最后进行比较。

代码

  1. head.h
/*************************************************************
文件名: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); 
};
  1. main.cpp
/*************************************************************
文件名: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;
}
  1. generate.cpp
/*************************************************************
文件名: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);
}
  1. verify.cpp
/*************************************************************
文件名: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<<"道题";	
}

运行测试

由于我们使用生成随机数的方法生成运算符号,并且将各种功能模块化设计,所以我们的程序可以自行选择计算的难度,是否出现乘除,括号分数;而且不只有四个数字,可能出现很多数字在同一个算式中,不仅小学生可以用,初中生,高中生也可以使用!.


提交记录

由于我主要是将代码先发给队友,在与他的代码兼容后才能在github上发布,所以提交记录不是很多

合作证据

这是我在最开始写好的三个文件,用QQ给队友发了过去

原文地址:https://www.cnblogs.com/gjx031602211/p/6405532.html