第三次寒假作业1.0
GitHub develop分支
GitHub master
队员:
我和我的队友计划先写一篇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静态变量,所以我主要讲生成题目和检验结果。
- 生成题目
首先分析完这道题后有以下几个难点:
- 随机数的生成(想要学习的话可以看我队友的博客。如何在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 ,通过循环让这种类型的式子进行随机组合。
- 检验结果
我写程序时最头疼的就是这个问题,因为根本没有思路。后来在队友的提示下在生成题目时就将题目存储到不同的变量中ans和result,最后进行比较。
代码
- 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);
};
- 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;
}
- 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);
}
- 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给队友发了过去