结对项目:自动生成四则运算表达式
一、Github项目地址:https://github.com/fangxiao-99/xiangmu2/
合作者:软工一班 3118004983 张方俊 3118004963 黄岳康
二、效能分析:
一开始想的是将自然数和真分数分开进行计算,生成表达式时再合在一起计算,后面讨论我们决定使用统一的结构体把自然数和真分数统计到一起,这样的话方便实现算法。
实现算法的时候初始是打算通过判断每个运算数的分子分母来进行不同格式的printf输出,但发现这样实现的效率较低且代码非常繁琐,后面讨论的时候把运算数统一格式,再通过判断运算符的个数进行不同的格式输出。
三、设计实现过程:
(1)生成四则运算题目
1.先确定结构体,用带分数的形式表示
2.写出加减乘除四则运算的函数,再根据需要进行调用
3.写出确定生成的表达式中运算符的个数的函数
4.根据运算符的个数,写出对表达式进行运算的函数
5.对生成的题目答案存入数组,每次生成时判断答案,若答案相同则不重复生成题目
6.最后根据运算符的个数,进行文件的输出
(2)对给定的题目文件和答案文件,判定答案中的对错并进行数量统计
1.用文件操作类函数确定题目文件中的题数,作为循环结束标志
2.fscanf函数获取题目文件和答案文件=号后面的字符串并存储起来
3.用strcmp比较字符串,将正确的题号和错误的题号存储起来
4.循环输出题号
四、代码说明:
1.结构体及变量定义
#include<stdio.h> #include<stdlib.h> #include<math.h> int chachong[10000][2]={(0,0)}; //查重的数组, 储存答案,答案相同则视为相同题目 int ysf=0; //题目中的运算符个数 char sig[3]; //存储运算符的数组 char getSignal() //获得运算符的函数 { char signal[4]={'+','-','*','/'}; return signal[rand()%4]; } typedef struct fen{ //分数结构体声明 int q=0; int fenzi=0; int fenmu=1; }fenshu; fenshu a1,a2,a3,a4,a5; void yuefen(int &m, int &n) //约分 { int p; p = (m + n - abs(m - n)) / 2;//p为m,n中较小值 for (int i = 2; i <= p; i++) if (m%i == 0 && n%i == 0) { m /= i; n /= i; i = 2; } }
2.加减乘除函数
fenshu jia(fenshu a1, fenshu a2) { //加法 fenshu a9; int f, g; f = a1.fenzi * a2.fenmu + a1.fenmu * a2.fenzi; g = a1.fenmu * a2.fenmu; yuefen(f, g); a9.fenzi = f; a9.fenmu = g; return a9; } fenshu jian(fenshu a1, fenshu a2) { //减法 fenshu a9; int f, g; f = a1.fenzi * a2.fenmu - a1.fenmu * a2.fenzi; g = a1.fenmu * a2.fenmu; yuefen(f, g); a9.fenzi = f; a9.fenmu = g; return a9; } fenshu chu(fenshu a1, fenshu a2) { //除法 fenshu a9; int f, g; f = a1.fenzi * a2.fenmu; g = a1.fenmu * a2.fenzi; yuefen(f, g); a9.fenzi = f; a9.fenmu = g; return a9; } fenshu cheng(fenshu a1, fenshu a2) { //乘法 fenshu a9; int f, g; f = a1.fenzi * a2.fenzi; g = a1.fenmu * a2.fenmu; yuefen(f, g); a9.fenzi = f; a9.fenmu = g; return a9; }
3.文件输出函数
void shuchu(char a, char b, char c, fenshu a1, fenshu a2, fenshu a3, fenshu a4, fenshu a5, int number, int max, FILE *fp, FILE *bp) { //根据ysf的值用switch函数输出 switch(ysf) { case 1: if (a5.fenzi < a5.fenmu) { fprintf(fp, "%d.四则运算题目%d'%d/%d %c %d'%d/%d = %d/%d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu,a1.fenmu,a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu,a2.fenmu,a5.fenzi,a5.fenmu); fprintf(bp, "%d.答案=%d/%d\n", number, a5.fenzi, a5.fenmu); break; } else if (a5.fenmu == 1) { fprintf(fp,"%d.四则运算题目%d'%d/%d %c %d'%d/%d = %d\n",number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu,a1.fenmu,a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu,a2.fenmu, a5.fenzi); fprintf(bp, "%d.答案=%d\n", number, a5.fenzi); break; } else { fprintf(fp, "%d.四则运算题目%d'%d/%d %c %d'%d/%d = %d'%d/%d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu,a1.fenmu,a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu,a2.fenmu,a5.fenzi/a5.fenmu,a5.fenzi%a5.fenmu,a5.fenmu); fprintf(bp, "%d.答案=%d'%d/%d\n", number, a5.fenzi / a5.fenmu, a5.fenzi%a5.fenmu, a5.fenmu); break; } break; case 2: if (a5.fenzi < a5.fenmu) { fprintf(fp, "%d .四则运算题目%d'%d/%d %c %d'%d/%d %c %d'%d/%d = %d/%d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu,a1.fenmu, a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu,a2.fenmu, b,a3.fenzi/a3.fenmu,a3.fenzi%a3.fenmu, a3.fenmu, a5.fenzi, a5.fenmu); fprintf(bp, "%d .答案=%d/%d\n", number, a5.fenzi, a5.fenmu); } else if (a5.fenmu == 1) { fprintf(fp, "%d .四则运算题目%d'%d/%d %c %d'%d/%d %c %d'%d/%d = %d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu,a1.fenmu, a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu,a2.fenmu, b,a3.fenzi/a3.fenmu,a3.fenzi%a3.fenmu, a3.fenmu, a5.fenzi); fprintf(bp, "%d.答案=%d\n", number, a5.fenzi); } else { fprintf(fp, "%d .四则运算题目%d'%d/%d %c %d'%d/%d %c %d'%d/%d = %d'%d/%d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu,a1.fenmu, a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu,a2.fenmu, b,a3.fenzi/a3.fenmu,a3.fenzi%a3.fenmu, a3.fenmu, a5.fenzi / a5.fenmu, a5.fenzi%a5.fenmu, a5.fenmu); fprintf(bp, "%d.答案=%d'%d/%d\n", number, a5.fenzi / a5.fenmu, a5.fenzi%a5.fenmu, a5.fenmu); } break; case 3: if (a5.fenzi < a5.fenmu) { fprintf(fp, "%d .四则运算题目%d'%d/%d %c %d'%d/%d %c %d'%d/%d %c %d'%d/%d = %d/%d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu, a1.fenmu, a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu, a2.fenmu, b,a3.fenzi/a3.fenmu,a3.fenzi%a3.fenmu, a3.fenmu, c,a4.fenzi/a4.fenmu,a4.fenzi%a4.fenmu, a4.fenmu, a5.fenzi, a5.fenmu); fprintf(bp, "%d .答案=%d/%d\n", number, a5.fenzi, a5.fenmu); } else if (a5.fenmu == 1) { fprintf(fp, "%d .四则运算题目%d'%d/%d %c %d'%d/%d% c% d'%d/%d %c %d'%d/%d=%d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu, a1.fenmu, a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu, a2.fenmu, b,a3.fenzi/a3.fenmu,a3.fenzi%a3.fenmu, a3.fenmu, c,a4.fenzi/a4.fenmu,a4.fenzi%a4.fenmu, a4.fenmu, a5.fenzi); fprintf(bp, "%d.答案=%d\n", number, a5.fenzi); } else { fprintf(fp, "%d .四则运算题目%d'%d/%d %c %d'%d/%d %c %d'%d/%d %c %d'%d/%d = %d'%d/%d\n", number,a1.fenzi/a1.fenmu,a1.fenzi%a1.fenmu, a1.fenmu, a,a2.fenzi/a2.fenmu,a2.fenzi%a2.fenmu, a2.fenmu, b,a3.fenzi/a3.fenmu,a3.fenzi%a3.fenmu, a3.fenmu, c,a4.fenzi/a4.fenmu,a4.fenzi%a4.fenmu, a4.fenmu, a5.fenzi / a5.fenmu, a5.fenzi%a5.fenmu, a5.fenmu); fprintf(bp, "%d.答案=%d'%d/%d\n", number, a5.fenzi / a5.fenmu, a5.fenzi%a5.fenmu, a5.fenmu); } break; default: printf("输入文件失败"); break; } }
4.表达式计算函数
fenshu jisuan(char a, fenshu a1, fenshu a2, fenshu a5) { switch (a) { case '+': //加法 a5 = jia(a1,a2); yuefen(a5.fenzi, a5.fenmu); break; case '-': a5 = jian(a1, a2); //减法 yuefen(a5.fenzi, a5.fenmu); break; case '*': a5 = cheng(a1, a2); //乘法 yuefen(a5.fenzi, a5.fenmu); break; case '/': a5 = chu(a1, a2); //除法 yuefen(a5.fenzi, a5.fenmu); break; } return a5; } fenshu jisuan2(char a, char b, fenshu a1, fenshu a2, fenshu a3, fenshu a5, fenshu a6) { if (a== '*' || a == '/') { if (a == '*') { a6 = cheng(a1, a2); yuefen(a6.fenzi, a6.fenmu); } else { a6 = chu(a1, a2); yuefen(a6.fenzi, a6.fenmu); } if (b == '*') { a5 = cheng(a6, a3); yuefen(a5.fenzi, a5.fenmu); } else if (b == '/') { a5 = chu(a6, a3); yuefen(a5.fenzi, a5.fenmu); } else if (b == '+') { a5 = jia(a6, a3); yuefen(a5.fenzi, a5.fenmu); } else { a5 = jian(a6, a3); yuefen(a5.fenzi, a5.fenmu); } } else { if (b== '*') { a6 = cheng(a2, a3); yuefen(a6.fenzi, a6.fenmu); if (a == '+') { a5 = jia(a1, a6); yuefen(a5.fenzi, a5.fenmu); } else { a5 = jian(a1, a6); yuefen(a5.fenzi, a5.fenmu); } } else if (b== '/') { a6 = chu(a2, a3); yuefen(a6.fenzi, a6.fenmu); if (a == '+') { a5 = jia(a1, a6); yuefen(a5.fenzi, a5.fenmu); } else { a5 = jian(a1, a6); yuefen(a5.fenzi, a5.fenmu); } } else if (b == '+'||b=='-') { if (a == '+') { a6 = jia(a1, a2); yuefen(a6.fenzi, a6.fenmu); if (b == '+') { a5 = jia(a6, a3); yuefen(a5.fenzi, a5.fenmu); } else { a5 = jian(a6, a3); yuefen(a5.fenzi, a5.fenmu); } } else { a6 = jian(a1, a2); yuefen(a6.fenzi, a6.fenmu); if (a == '+') { a5 = jia(a6, a3); yuefen(a5.fenzi, a5.fenmu); } else { a5 = jian(a6, a3); yuefen(a5.fenzi, a5.fenmu); } } } } return a5; } fenshu jisuan3(char a, char b, char c, fenshu a1, fenshu a2, fenshu a3, fenshu a4, fenshu a5) { fenshu a6, a7; if (a== '/' || a == '*') { if (a == '*') { a6 = cheng(a1, a2); yuefen(a6.fenzi, a6.fenmu); } else { a6 = chu(a1, a2); yuefen(a6.fenzi, a6.fenmu); } a5 = jisuan2(b, c, a6, a3, a4, a5, a7); } else if (b == '/' || b == '*') { if (b == '*') { a6 = cheng(a2, a3); yuefen(a6.fenzi, a6.fenmu); } else { a6 = chu(a2, a3); yuefen(a6.fenzi, a6.fenmu); } a5 = jisuan2(a, c, a1, a6, a4, a5, a7); } else if (c == '/' || c == '*') { if (c == '*') { a6 = cheng(a3, a4); yuefen(a6.fenzi, a6.fenmu); } else { a6 = chu(a3, a4); yuefen(a6.fenzi, a6.fenmu); } a5 = jisuan2(a, b, a1, a2, a6, a5, a7); } else { if (a == '+') { a6 = jia( a1, a2); yuefen(a6.fenzi, a6.fenmu); } else { a6 = jian(a1, a2); yuefen(a6.fenzi, a6.fenmu); } a5 = jisuan2(a, b, a6, a3, a4, a5, a7); } return a5; }
5.主函数
int main() { int n, r,a=0; int flag=0; printf("请输入您要生成的题目的数量-n x\n"); scanf("%d", &n); getchar(); printf("请输入随机数的范围 x\n"); scanf("%d", &r); getchar(); FILE *fp; if ((fp = fopen("exercises1.txt", "w")) == NULL) { printf("打不开exercises1.txt!\n"); exit(0); } FILE *bp; if ((bp = fopen("answers1.txt", "w")) == NULL) { printf("打不开answers1.txt!\n"); exit(0); } int i = 0, number = 1; while (i < n) { fenshu a6; shengcheng(r); if (ysf == 1) { a5=jisuan(sig[0], a1, a2, a5); if (a5.fenzi < 0) continue; while (a<number) { if(chachong[a][0]==a5.fenzi&&chachong[a][1]==a5.fenmu) { a++; flag=1; break; } chachong[a][0]=a5.fenzi; chachong[a][1]=a5.fenmu; a++; flag=0; } if(flag==1) continue; shuchu(sig[0], sig[1], sig[2], a1, a2, a3, a4, a5, number, r, fp, bp); } else if (ysf== 2) { a5 = jisuan2(sig[0], sig[1], a1, a2, a3, a5, a6); if (a5.fenzi < 0) continue; while (a<number) { if(chachong[a][0]==a5.fenzi&&chachong[a][1]==a5.fenmu) { a++; flag=1; break; } chachong[a][0]=a5.fenzi; chachong[a][1]=a5.fenmu; a++; flag=0; } if(flag==1) continue; shuchu(sig[0], sig[1], sig[2], a1, a2, a3, a4, a5, number, r, fp, bp); } else { a5 = jisuan3(sig[0], sig[1], sig[2], a1, a2, a3, a4, a5); if (a5.fenzi < 0) continue; while (a<number) { if(chachong[a][0]==a5.fenzi&&chachong[a][1]==a5.fenmu) { a++; flag=1; break; } chachong[a][0]=a5.fenzi; chachong[a][1]=a5.fenmu; a++; flag=0; } if(flag==1) continue; shuchu(sig[0], sig[1], sig[2], a1, a2, a3, a4, a5, number, r, fp, bp); } i++; number++; } fclose(fp); fclose(bp); return 0; printf("生成成功\n"); }
五、测试说明
1.生成四则运算题目
输入到文件中
2.判断题目文件答案与答案文件的答案是否一致
题目文件:
答案文件:
判断结果:
六、PSP表格
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 |
70 |
· Estimate |
· 估计这个任务需要多少时间 |
5760 |
6000 |
Development |
开发 |
5760 |
5760 |
· Analysis |
· 需求分析 (包括学习新技术) |
120 |
120 |
· Design Spec |
· 生成设计文档 |
30 |
30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
0 |
0 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
60 |
60 |
· Design |
· 具体设计 |
20 |
20 |
· Coding |
· 具体编码 |
5000 |
5000 |
· Code Review |
· 代码复审 |
60 |
60 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
15 |
15 |
Reporting |
报告 |
10 |
10 |
· Test Report |
· 测试报告 |
0 |
0 |
· Size Measurement |
· 计算工作量 |
10 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
20 |
20 |
合计 |
|
5820 |
6070 |
七、项目小结:
张方俊:第一次用结对项目感觉挺新奇的,我觉得好处在于写同一项目时我们可以得到思想上的交流,讨论出更好的项目思路和代码结构,缺点的话就是在码的时候很难做到统一,所以我和岳康在写之前先进行讨论,把整体的思路和定义定下来,再分开实现功能。因为我有参与功能9的实现,我学到的是在使用文件操作类函数时需要注意的几点,fprintf和fgetc和fscanf函数都是会改变文件流中的位置指针,所以我在一开始获取行数时位置指针没有复原,导致下面的fscanf无法获得字符串,还有就是在判断EOF时,fgetc函数的返回值是一个字节,若c!=EOF中c为char类型,读到字符0xFF,与EOF的值-1,即0xFF(char类型的-1表达形式)比较结果为相等,此时会导致判断失误,程序提早退出,故正确用法应把c定义为int类型,就不会判断失误。
黄岳康:刚接到结对项目的时候,就觉得如果不先讨论清楚,难度可能比个人实现更麻烦。所以我跟方俊第一步就先讨论好大致的思路,包括实现方法,变量名和数据结构等等。做下来分歧会少很多,但也会有。遇到分歧的时候我们就择优。在合作过程中能互相学习,提高。这次结对项目也重温了形参和实参的传递和return不能返回多个值或者结构体。