结对项目:四则运算(C语言)

github地址:https://github.com/nilonger/arithmetic

结对伙伴:杨锐龙+黄海钊

一、项目要求

1.1 题目:实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。

1.2 说明:

  • 真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。

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

  • 运算符:+, −, ×, ÷。

  • 括号:(, )。

  • 等号:=。

  • 分隔符:空格(用于四则运算符和等号前后)。

  • 算术表达式:

    e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),

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

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

1.3 项目要求

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

  • (完成) 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如 Myapp.exe -r 10 将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

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

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

  • (完成) 每道题目中出现的运算符个数不超过3个。

  • 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,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+3和3+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。

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

   答案1

    答案2

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

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

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

  Myapp.exe -e .txt -a .txt 统计结果输出到文件Grade.txt,格式如下:

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

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

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


 二、PSP表

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

100

 200

· Estimate

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

60

 70

Development

开发

6*24*60

 5*24*60

· Analysis

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

60

 50

· Design Spec

· 生成设计文档

20

 20

· Design Review

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

30

 30

· Coding Standard

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

20

20

· Design

· 具体设计

100

 150

· Coding

· 具体编码

4*24*60

4*24*60

· Code Review

· 代码复审

100

 150

· Test

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

40

60

Reporting

报告

20

20

· Test Report

· 测试报告

10

 30

· Size Measurement

· 计算工作量

10

 30

· Postmortem & Process Improvement Plan

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

30

 30

合计

 

6*24*60

5*24*60

三、解题思路

1、先实现生成式子的功能。

(1)先写好生成操作数和包含1~3个操作符的式子的四个函数,再通过一个函数一起调用,随机生成带括号的式子

(2)除号和乘号÷、×先用*、#代替

(3)C语言里面的除号和乘号÷、×不能直接从文件读出来,所以就放弃了从文件里读出来计算的想法,改为生成的时候,把式子放进数组再来计算,计算不小于0,才把数组存进文件,存进文件的时候把*、#这两个符号替换成÷、×

2、计算功能。

(1)只定义了一个分数结构体,因为想到计算的时候 整数也可以转化为分数,如2可以化为2/1

(2)定义两个栈,手撸栈的各个操作(C语言没办法),一个栈用来压入分数,一个用来压入操作符

3、比较题目文件,判断答案文件中的对错。

(1)这个可以说是取巧了一下,因为文件的生成,帮随着答案的生成,所以比较的是 标准答案文件 和 新答案文件

(2)原本是想从文件里面再读出式子拿来计算,但是写进文件后有了符号÷、×,但是那两个符号÷、×读出来会出错,因为他们不是普通字符型数据的长度。

4、主函数。

用输入参数的方式在cmd运行,很好的分开了生成式子(同时生成答案)和比较答案这两个步骤

四、设计实现过程

结对项目:四则运算(C语言)

五、代码说明

头文件和相关结构体和栈的定义:

#include<stdio.h>
#include<stdlib.h>
#include <time.h>
#include<string.h>
#include<io.h>
#include<math.h>
#define MINSIZE 256
#define MAXSIZE 1024
#define OK 1
#define ERROR 0


int r,n;   //生成数的最大值 和 式子数量 
typedef int Status;

char* oneOperator();
char* creatOperator();
char* threeOperator();
char* twoOperator();
char *creatFormula(int y);
char* creatSnum();
Status Answer_Sq(char formula[],int y);

typedef struct StackNode{    //分数 栈结点
    int fm;    //分母
    int fz;    //分子
    struct StackNode *next;
}node1;

typedef struct Stackop {  //运算符 栈结点

    int data;            
    struct Stackop *next;
}node2;

typedef struct stack1  { //分数栈

    node1 *top;
    int length;
}StackSq1;

typedef struct stack2  {  //运算符栈

    node2 *top;
    int length;
}StackSq2;

栈的相关操作:

Status InitStack_Sq1(StackSq1 &S){   //初始化 运算数 的栈 

    S.top=NULL;
    S.length=0;
    return OK; 
}

Status InitStack_Sq2(StackSq2 &S){    //初始化 运算符 的栈 

    S.top=NULL;
    S.length=0;
    return OK;
}

Status StackEmpty_Sq(StackSq2 S){  // 对  运算符 判空,若为空则返回TURE,否则返回FALSE 
    if(S.length==0) return OK;
    else return ERROR;
}

Status Push_Sq1(StackSq1 &S,int fenzi,int fenmu){   //分数 进栈 
    node1 *p;
    p=(node1 *)malloc(sizeof (node1));
    p->fm=fenmu;
    p->fz=fenzi;
    p->next=S.top;
    S.top=p;
    S.length++;
    return OK; 
} 

Status Push_Sq2(StackSq2 &S,int e){   //运算符 进栈 
    node2 *p;
    p=(node2 *)malloc(sizeof (node2));
    p->data=e;
    p->next=S.top;
    S.top=p;
    S.length++;
    return OK; 
} 

node1 Pop_Sq1(StackSq1 &S){  //记得类型是 node1 
    node1 A;
    node1 *p=S.top;
    A.fm=p->fm;
    A.fz=p->fz;
    S.top=p->next;
    free(p);
    S.length--;
    return A;
} 

Status Pop_Sq2(StackSq2 &S){   //  运算符栈顶  出栈 
    int e;
    node2 *p=S.top;
    e=p->data;
    S.top=p->next;
    free(p);
    S.length--;
    return e;
} 

Status Get_Top(StackSq2 S){     //取  运算符 栈顶元素 (不出栈) 
    if(S.top==NULL)return ERROR;
    return S.top->data;
}

生成式子的相关函数:

char* creatOperator() //生成运算符
{
    srand((unsigned)time(NULL) + rand());
    char* c = new char[2];
    int a = rand() % 4;
    //int a = 1;
    switch (a)
    {
    case 1:  strcpy(c, "+");  break;
    case 2: strcpy(c, "-"); break;
    case 3: strcpy(c, "*"); break;
    case 0:  strcpy(c, "#"); break;
    default:
        break;
    }
    return c;
}

char* creatSnum()  //生成运算数 
{
    srand((unsigned)time(NULL) + rand());
    char* string = NULL;
    char string1[MAXSIZE] = {};
    string = string1;
    char c[MAXSIZE] = {};
    int tag = rand() % 2;
    int num1=0,num2=0;
    if (tag == 0)
    {
        num1 = rand() % (r)+1;    //不要(size+1),保证整数不为  0 
        sprintf(c, "%d", num1);
        strcat(string1, c);
        
    }
    else
    {
        num1 = rand() %(r-1) ;   //最大为 m-2,方便后面算法 
        
        if (num1 != 0)
        {
            sprintf(c, "%d", num1);
            strcat(string1, c);
            strcat(string1, "/");
        }
        while (num2 == 0 || num2 <= num1)
           {
              num2 = rand() % r;    //最大为 m-1 
           }
        sprintf(c, "%d", num2);
        strcat(string1, c);

    }
    return string;
}

char* oneOperator()  //一个操作符的式子
{
    srand((unsigned)time(NULL) + rand());
    char string[MINSIZE] = {};
    char* posture = string;
    char c[MINSIZE] = {};        
    strcpy(c, creatSnum());
    strcat(string, c);
    
    strcpy(c,creatOperator());
    strcat(string, c);
    //printf("%s
", string);

    strcpy(c, creatSnum());
    strcat(string, c);
    //printf("%s
", string);
    return posture;
}

char* twoOperator() //两个操作符得式子
{
    srand((unsigned)time(NULL) + rand());
    int flag = 0;
    int tag=0;
    char string[128] = {};
    char* posture = string;
    char c[MAXSIZE] = {};

    flag = rand() % 2;
    if (flag == 0)
    {
        strcpy(c, "(");
        strcat(string, c);
        tag = 1;
    }

    strcpy(c, creatSnum());
    strcat(string, c);

    strcpy(c,creatOperator());
    strcat(string, c);

    strcpy(c, creatSnum());
    strcat(string, c);

    flag = rand() % 2;
    if (flag == 0)
    {
        if (tag == 1)
        {
            strcpy(c, ")");
            strcat(string, c);
        }
        tag = 0;
    }

    strcpy(c,creatOperator());
    strcat(string, c);

    strcpy(c, creatSnum());
    strcat(string, c);

    if (tag == 1)
    {
        strcpy(c, ")");
        strcat(string, c);
    }

    //printf("%s
", string);
    return posture;
}


char* threeOperator()  //三个操作符得式子
{
    srand((unsigned)time(NULL) + rand());
    char string[128] = {};
    char* posture = string;
    char c[MAXSIZE] = {};

    strcpy(c, creatSnum());
    strcat(string, c);

    strcpy(c,creatOperator());
    strcat(string, c);

    strcpy(c, creatSnum());
    strcat(string, c);

    strcpy(c,creatOperator());
    strcat(string, c);

    strcpy(c, creatSnum());
    strcat(string, c);

    strcpy(c,creatOperator());
    strcat(string, c);

    strcpy(c, creatSnum());
    strcat(string, c);

    return posture;
}

char pan[5]={"-1"};
char *mp=pan;
char duan[5]={"1"};
char *np=duan;

char *creatFormula(int y)   //生成式子
{    
     srand((unsigned)time(NULL) + rand());
//    char op[3];
    char string[MAXSIZE] = {};
    char* posture = string;
    int a = rand() % 3;    //生成的随机数,即运算符个数
    //printf("%d
", a);

    FILE *fp;
    fp=fopen("test.txt","a");
    int i=0;
    
    char divi[5]={"÷"};  //用于后面存进文件 
    char mult[5]={"×"};
    
    switch (a)
    {
    case 0:strcpy(string, oneOperator());         
//            printf("%s
",posture);   //和文件作比较   
            break;
    
    case 1:strcpy(string, twoOperator());
//            printf("%s
",posture);
            break;
            
    case 2:strcpy(string, threeOperator());
//            printf("%s
",posture); 
            break;
             
    default:break;
    }
    if(Answer_Sq(posture,y)<0)    //计算的时候用  数组 算,打印则要转化一下 
    {
        fclose(fp);
        return mp;   // 因为是 char 型函数,不能直接返回-1 
    }
    else {
//        fprintf(fp,"%d.%s = 
",y,posture);
        fprintf(fp,"%d.",y);
        while(string[i]!='