结对四则运算编程(5212邓画月+5213何颖琪)

一:Github项目地址 https://github.com/vicky-3653/operation

项目负责人:邓画月 何颖琪

 

题目:实现一个自动生成小学四则运算题目的命令行程序。

说明:

自然数:0, 1, 2, …。

  • 真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
  • 运算符:+, −, ×, ÷。
  • 括号:(, )。
  • 等号:=。
  • 分隔符:空格(用于四则运算符和等号前后)。
  • 算术表达式:e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),

其中e, e1和e2为表达式,n为自然数或真分数。

  • 四则运算题目:e = ,其中e为算术表达式。

需求:

1. 使用 -n 参数控制生成题目的个数,例如Myapp.exe -n 10将生成10个题目。

2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如Myapp.exe -r 10

 将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2

4. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数

5. 每道题目中出现的运算符个数不超过3个。

6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+33+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。

生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:

1. 四则运算题目1

2. 四则运算题目2

……

其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:

1. 答案1

2. 答案2

特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

8. 程序应能支持一万道题目的生成。

9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:

Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt

统计结果输出到文件Grade.txt,格式如下:

Correct: 5 (1, 3, 5, 7, 9)

Wrong: 5 (2, 4, 6, 8, 10)

其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

二: PSP2.1表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 180180 

· Estimate

· 估计这个任务需要多少时间

 19502300

Development

开发

 120 120

· Analysis

· 需求分析 (包括学习新技术)

 600 600

· Design Spec

· 生成设计文档

 30 40

· Design Review

· 设计复审 (和同事审核设计文档)

 30 40

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 30 30

· Design

· 具体设计

 200 180

· Coding

· 具体编码

 400 630

· Code Review

· 代码复审

 120 200

· Test

· 测试(自我测试,修改代码,提交修改)

 180 120

Reporting

报告

 120 120

· Test Report

· 测试报告

 50 50

· Size Measurement

· 计算工作量

 40 40

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 30 30

合计

   2300

三:效能分析

性能分析图:

 

在分析设计过程中,选择先实现题目与答案文件的打印,后来发现难实现对比练习文件和答案文件,

所以选择了另外一种实现方法,让用户选择程序的运行,分别是出练习题和对答案。

四:设计实现过程和代码说明

主函数:

主函数实现了选择功能的框架,并实现了参数必须给定的要求,否则程序报错并给出帮助信息。

 

  1 int main()
  2 {   
  3     FILE *fq;//题目的文档 
  4     FILE *fa;//答案的文档 
  5     int x; 
  6     printf("选择序号:\n1.出题目\n2.对答案\n");
  7     scanf("%d",&x);
  8     while(x!=1 && x!=2)//选择功能 
  9     {
 10     printf("重新输入功能!\n");
 11     scanf("%d",&x);
 12     if(x!=1 && x!=2)
 13     continue;
 14     }
 15     switch(x)
 16     {
 17     case 1:
 18     {
 19     if((fq=fopen("Exercises.txt","w"))==NULL){ 
 20     printf("Cannot open this file!\n");
 21     exit(0);
 22     } 
 23     if((fa=fopen("Answers.txt","w"))==NULL){ 
 24     printf("Cannot open this file!\n");
 25     exit(0);
 26     } 
 27     srand(time(0));
 28     int m;
 29     int n,r;
 30     printf("请输入题目个数\n");
 31     printf("Myapp.exe -n ");
 32     while(1){//该参数必须给定,否则程序报错并给出帮助信息。
 33         if(scanf("%d",&n)==1)break;
 34         else printf("请重新输入1或者其他自然数:");
 35         fflush(stdin);
 36     }
 37     
 38     printf("请输入题目中数值的范围\n");
 39     printf("Myapp.exe -r ");
 40     while(1){//该参数必须给定,否则程序报错并给出帮助信息。
 41         if(scanf("%d",&r)==1)break;
 42         else printf("请重新输入1或者其他自然数:");
 43         fflush(stdin);
 44     }
 45      
 46     
 47     //循环出题
 48     for(int i=0;i<n;i++){        
 49           m=rand()%3+1;//m=1or2or3
 50           printf("%d.",i+1);
 51           fprintf(fq,"%d.",i+1);
 52           fprintf(fa,"%d.",i+1);
 53         switch(m){
 54             case 1:{
 55                 print1(r,fq,fa);
 56                 printf("\n");
 57                 fprintf(fq,"\n");
 58                 fprintf(fa,"\n");
 59                 break;
 60             }
 61             case 2:{
 62                 print2(r,fq,fa);
 63                 printf("\n"); //打印函数2
 64                 fprintf(fq,"\n");
 65                 fprintf(fa,"\n");
 66                 break;
 67             }
 68             case 3:{                
 69                 print3(r,fq,fa);//打印函数3
 70                 printf("\n");
 71                 fprintf(fq,"\n"); 
 72                 fprintf(fa,"\n");
 73                 break;
 74             }
 75         }
 76     } 
 77     fclose(fq);
 78     fclose(fa);
 79     break;
 80 }
 81 //对答案
 82     case 2 :
 83     {FILE *ans;
 84     FILE *stu; 
 85     if((ans=fopen("Answers.txt","r"))==NULL){ 
 86     printf("Cannot open this file!\n");
 87     exit(EXIT_FAILURE);
 88     } 
 89     if((stu=fopen("Student.txt","r"))==NULL){ 
 90     printf("Cannot open this file!\n");
 91     exit(EXIT_FAILURE);
 92     }    
 93     int r;
 94     printf("一共有多少题:");
 95     scanf("%d",&r);
 96     check(ans,stu,r); 
 97 
 98     break;
 99     }
100  
101      return 0;
102 } 
103 }

练习题文件和答案文件的打印:

练习题和答案分成了case1,2,3,分别对应两个分数的运算,两个整数的运算,一个整数一个分数的运算,通过主函数随机调用print123,达到随机生成不同运算。

运算式的生成中,分母不能为0,不能生成答案为负的式子。

我们另外调用了分数和整数的比较函数frac_fracompare,分数比较函数frac_fraccompare,分数显示函数show,去分子分母公约数函数simple。

获取随机数的函数运用到了rand()%。

void print1(int r,FILE *fq,FILE *fa){//两个都是分数的打印 
   int a,b,c,d,f,g,h;
   char e; 

   a=Creat_Int(r);
   b=Creat_Int(r);
   c=Creat_Int(r);
   d=Creat_Int(r);
   
    while(b==0){
        b=Creat_Int(r);
        //b=rand()%r; 
        break;
    } 
    while(d==0){
        d=Creat_Int(r);
        //d=rand()%r; 
        break;
    }
   
   e=getSignal();

   simple(&a,&c);
   simple(&b,&d);
   
   frac_fraccompare(&a,&b,&c,&d);//若a/c < b/d 调换位置 
   
//显示产生的算式
show(a,c,r,fq);//经常会显示分数 
switch(e) 
{
     case '+': {
         printf(" + ");
         fprintf(fq," %c ",e);
        break;
     }
     case '-': {
         printf(" - ");
         fprintf(fq," %c ",e);
        break;
     }
     case '*': {
         printf(" * ");
         fprintf(fq," %c ",e);
        break;
     }
     case '/':{
         printf(" / ");
         fprintf(fq," %c ",e);
        break;
     }
}
show(b,d,r,fq);
printf(" = ");
fprintf(fq," = ");


switch(e) //答案 
{
    case '+': 
     f=a*d+b*c;
     g=c*d;
     simple(&f,&g);
     show(f,g,r,fa);
     break;

    case '-':
     f=a*d-b*c;
     g=c*d;
     simple(&f,&g);
     show(f,g,r,fa);
     break;
      
     case '*': 
      f=a*b;
      g=c*d;
      simple(&f,&g);
      show(f,g,r,fa); 
      break;
     
     case '/': 
      f=a*d;
      g=b*c;
      simple(&f,&g);
      show(f,g,r,fa); 
     break;
}    
}
void print2(int r,FILE *fq,FILE *fa){//两个都是整数的打印 
    int num1,num2;
    char signal;
    signal=getSignal();
    
    num1=Creat_Int(r);
    num2=Creat_Int(r);
    
    switch(signal){
        case '+':{
            printf("%d + %d = ",num1,num2);
            fprintf(fq,"%d + %d = ",num1,num2);
            break;
        }
        case '-':{
            if(num1<num2){
                int t;
                t=num1;
                num1=num2;
                num2=t;
            }
            printf("%d - %d = ",num1,num2);
            fprintf(fq,"%d - %d = ",num1,num2);
            break;
        }
        case '*':{
            printf("%d * %d = ",num1,num2);
            fprintf(fq,"%d * %d = ",num1,num2);
            break;
        }
        case '/':{
            while(num2==0){
                num2=Creat_Int(r);
            }
            if(num1>num2){
                int t;
                t=num1;
                num1=num2;
                num2=t;
            }
            printf("%d / %d = ",num1,num2);
            fprintf(fq,"%d / %d = ",num1,num2);
            break;
        }
        
    }
    
    //计算的
    switch(signal) 
{
case '+':
    int c;
    c=num1+num2;
    printf("%d",c);
    fprintf(fa,"%d",c);
    break;
case '-':
    c=num1-num2; 
    printf("%d",c);
    fprintf(fa,"%d",c);   
    break;
case '*': 
    c=num1*num2;
    printf("%d",c);
    fprintf(fa,"%d",c);
    break;
case '/':
    c=num1/num2;
    show(num1,num2,r,fa);
    break;
}

}
void print3(int r,FILE *fq,FILE *fa){//一整一分的打印 

    int fenzi,fenmu,num1;
    char signal;
    fenzi=Creat_Int(r);
    fenmu=Creat_Int(r);
    num1=Creat_Int(r);

    signal=getSignal();
    
    while(fenmu==0){
                fenmu=Creat_Int(r);
            }
    if(fenzi>fenmu){
                int t;
                t=fenzi;
                fenzi=fenmu;
                fenmu=t;
            }
    simple(&fenzi,&fenmu);    

    switch(signal){
        case '+':{
        printf("%d %c ",num1,signal);
        fprintf(fq,"%d %c ",num1,signal);
        show(fenzi,fenmu,r,fq);
        printf(" = ");
        fprintf(fq," = ");
        break;
        }
        case '-':{
        while(num1==0){
            num1=Creat_Int(r);
        }
        printf("%d %c ",num1,signal);
        fprintf(fq,"%d %c ",num1,signal);
        show(fenzi,fenmu,r,fq);
        printf(" = ");
        fprintf(fq," = ");
        break;
        }
        case '*':{
        printf("%d %c ",num1,signal);
        fprintf(fq,"%d %c ",num1,signal);
        show(fenzi,fenmu,r,fq);
        printf(" = ");
        fprintf(fq," = ");    
        break;
        }
        case '/':{
        while(fenzi==0){
        fenzi=Creat_Int(r);
        }
        printf("%d %c ",num1,signal);
        fprintf(fq,"%d %c ",num1,signal);
        show(fenzi,fenmu,r,fq);
        printf(" = ");
        fprintf(fq," = ");    
        break;
        }
    } 
            
    //计算的
    switch(signal){
        int x;
        case '+':{
            x=fenmu*num1+fenzi;
            simple(&x,&fenmu);
            show(x,fenmu,r,fa);
            break;
        }
        case '-':{        
            x=fenmu*num1-fenzi;
            simple(&x,&fenmu);
            show(x,fenmu,r,fa);
            break;
        } 
        case '*':{
            x=fenzi*num1;
            simple(&x,&fenmu);
            show(x,fenmu,r,fa);
            break;
        }
        case '/':{  
            x=fenmu*num1;
            simple(&fenzi,&x);
            show(x,fenzi,r,fa);
            break;
        }
    } 
        
}

随机获取符号函数和随机获取整数函数:

1 char getSignal(){//随机获取运算符函数 
2     char signal[4]={'+','-','*','/'};
3     //srand((unsigned)time(NULL));
4     //srand(time(0));
5     return signal[rand()%4];
6 }
7 int Creat_Int(int r){//随机获取整数函数
8     return rand()%r;
9 }

约去分子分母公约数函数:

void simple(int * m,int * n){ //约去分子分母公约数 需要用指针改数字,因为返回是void 
    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;               
    } 
    }
}

分数和整数的比较函数:

int frac_intcompare(int fenzi,int fenmu,int a){//分数和整数的比较函数 
    int d;
    int flag;
    d=fenmu*a;
    if((fenzi/d)>=1)flag=1;//分数(a/b)大于整数 
    else flag=0;
    return flag;
}

带又的真分数转换函数:

int show(int a,int b,int r,FILE *fp){//分数转换为带又的真分数 
    int c;
    while(b==0){
        b=Creat_Int(r); 
        //b=rand()%r;
    }
    
    c=a/b;
    if(a==0) {
        printf("%d",a);
        fprintf(fp,"%d",a);
    }
    else if(a%b==0) {
        printf("%d",c);
        fprintf(fp,"%d",c);
    }
    else if(c==0) {
        simple(&a,&b); 
        printf("%d/%d",a,b);
        fprintf(fp,"%d/%d",a,b);
    }
    else {
    a=a-b*c;
    simple(&a,&b); 
    printf("%d'%d/%d",c,a,b);
    fprintf(fp,"%d'%d/%d",c,a,b);
    }
}

调换位置函数:

void frac_fraccompare(int *fenzi1,int *fenzi2,int *fenmu1,int *fenmu2){//若a/c < b/d 调换位置 
    int m,n;
    int a,b;
    a=(*fenzi1) * (*fenmu2);
    b=(*fenmu1) * (*fenzi2);
    if(a<b){
        m=*fenzi1;
        *fenzi1=*fenzi2;
        *fenzi2=m;
        
        n=*fenmu1;
        *fenmu1=*fenmu2;
        *fenmu2=n;
    }
}
    

比较文件答案函数:

答案文件的比较,我们的思路是通过读写学生文件与原答案文件进行对比,不同的题目转换成正确和错误的数组,再通过数组形式输出right和wrong。

void check(FILE *ans,FILE *stu,int n){
    char a[n+1];
    char s[n+1];
    int right[n+1];
    int wrong[n+1];
    char *p;
    char *q;
    int num=0;//统计对的个数 
    
    for(int i=0,j=0,k=0;i<n;i++){
    p=fgets(a,n+1,ans);
    q=fgets(s,n+1,stu);
    if(strcmp(a,s)==0){
        right[j]=i+1;
        j++;
        num++;    
    }
    else{
        wrong[k]=i+1;
        k++;
    }
    p++;
    q++;
    }
    printf("\nCorrect:%d(",num);
    fprintf(stu,"\nCorrect:%d(",num);
    for(int j=0;j<num;j++){
        fprintf(stu,"%d ",right[j]);
        printf("%d ",right[j]);
    }
    printf(")\n");
    fprintf(stu,")\n");
    
    printf("\nWrong:%d(",n-num);
    fprintf(stu,"\nWrong:%d(",n-num);
    for(int k=0;k<n-num;k++){
        printf("%d ",wrong[k]);
        fprintf(stu,"%d ",wrong[k]);
    }
    printf(")\n");
    fprintf(stu,")\n");
    fclose(ans);
    fclose(stu);
}

没有实现的功能:

①每道题目中出现的运算符个数我们只实现了单个运算符。

②程序一次运行生成的题目不能重复。

程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt,统计结果输出到文件Grade.txt。但我们以另一种形式勉强实现了文件的答案比较。

 遇到的困难描述:

①数据结构不熟悉,没有运用结构体,我们的题目只能实现单个运算符。

②文件的读写格式不熟悉,导致fprintf不能正确打印,发现并已解决。

③细节的代码处理麻烦,比如减法,除法或者分数的话要判断它们的大小,然后调整数字。

五、测试代码:

①功能一的测试:

 

②功能二的测试:

 

程序能通过一万道题目的输出,但测试后发现程序运行时间比较长,还没有修改的想法。

六、小结

邓画月:

清楚的意识到没学好数据结构的后果是多么的严重。

何颖琪:

因为写代码的经验不足,在合作过程中我会出一些差错,但是我的队友能很好的帮助我,使我在这次合作收获了更多的知识,这是做单人项目的时候我感受不到的。还有我自身的经验不足,导致这次工程的实现勉勉强强,今后会更加努力的学习新的语言和复习运用好c语言。

整体:合作期间整体工作安排还不够恰当,没有充分发挥好分工合作的性能,但我们有更多的经验交流和想法表达空间。

 

转载于:https://www.cnblogs.com/vicky-3653/p/9732795.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值