结对作业

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


 

项目GitHub地址:https://github.com/LittleTaro/Calculator

结对人:舒雯钰(3218005129)、方子茵(3218005127)

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 30

 45

· Estimate

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

 30

 35

Development

开发

 1670

 2020

· Analysis

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

240

 520

· Design Spec

· 生成设计文档

 150

 135

· Design Review

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

 60

 50

· Coding Standard

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

 20

 40

· Design

· 具体设计

 180

 240

· Coding

· 具体编码

 600

 600

· Code Review

· 代码复审

 180

 135

· Test

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

 240

 300

Reporting

报告

 120

 240

· Test Report

· 测试报告

 60

 40

· Size Measurement

· 计算工作量

 30

 45

· Postmortem & Process Improvement Plan

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

 30

 65

合计

 

 1820

 2305



项目要求


使用 -n 参数控制生成题目的个数,例如:Myapp.exe -n 10  将生成10个题目。
使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如:Myapp.exe -r 10将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
每道题目中出现的运算符个数不超过3个。
程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:四则运算题目1
      2. 四则运算题目2
      3. ……
      其中真分数在输入输出时采用如下格式,真分数五分之三表示为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)



效能分析

 


 

代码设计

 

结对作业_第1张图片

 

结对作业_第2张图片

结对作业_第3张图片

结对作业_第4张图片

结对作业_第5张图片


 

关键代码

 

  1 #include "Calculator.h"
  2 //#include
  3 //#include
  4 //#include
  5 //#include
  6 //#include 
  7 //using namespace std;
  8 time_t g_unused = (srand(time(NULL)), 0);     // Generate a seed for 'rand' in whole program.
  9 
 10 const char* operators[4] = { " + ", " - ", " * ", " / " };
 11 
 12 int random(int a, int b) // 生成[a,b]间的随机整数
 13 {
 14     //srand((unsigned)time(NULL));
 15     return (rand() % (b - a + 1) + a);
 16 }
 17 
 18 char a[100] = "";
 19 char* op = a;     // 存放转换成由数字转换而来的字符串
 20 char b[100] = "";
 21 char* pro = b;  // 存放题目
 22 char c[100] = "";
 23 char* dem = c;//  存放转换成字符串的分母
 24 char d[100] = "";
 25 char* tmp = d;;// 存放转换成字符串的带分数整数部分
 26 
 27 
 28 char* GeneratePro()
 29 {
 30     int num = 0;          // 运算符个数,1~3个
 31     int index = 0;        // 指示运算符的下标
 32     num = random(1, 3);   // 随机生成运算符个数,1到3个
 33     int count = 0;
 34     int left_c = 0;      // 未匹配的左括号个数
 35     int tag = 0;         // 在操作数右边插入“)”指示该操作数左边是否插入左括号
 36     memset(pro, 0, sizeof(pro));  // 每调用一次,都要初始化内存空间
 37     //memset(tmp, 0, sizeof(tmp));  // 每调用一次,都要初始化内存空间
 38     int way = 0;    // 括号插入方式
 39     while (count < num)
 40     {
 41         // 先在此处判断是否插入“(”
 42         if (count == 0 && num > 1 && random(0, 1))
 43         {
 44             strcat_s(pro, strlen(pro) + 2, "(");    // 插入左括号
 45             left_c++;    // 未匹配的左括号个数加1
 46             tag = 1;
 47         }
 48         else if (count == 1 && num > 1 && random(0, 1) && !left_c)
 49         {
 50             strcat_s(pro, strlen(pro) + 2, "(");    // 插入左括号
 51             left_c++;    // 未匹配的左括号个数加1
 52             tag = 1;
 53         }
 54         else if (count == 2 && num > 2 && random(0, 1) && !left_c)
 55         {
 56             strcat_s(pro, strlen(pro) + 2, "(");    // 插入左括号
 57             left_c++;    // 未匹配的左括号个数加1
 58             tag = 1;
 59         }
 60         // 随机生成一个运算数(整数或分数),并将其转变为字符串形式
 61         op = GenerateOperand();//operand = GenerateOperand();
 62         strcat_s(pro, strlen(pro) + strlen(op) + 1, op);    // 将操作符移到指向题目的指针
 63         // 在此处判断是否插入“)”
 64         if (tag != 1 && ((count == num - 1) || random(0, 1)) && left_c)
 65         {
 66             strcat_s(pro, strlen(pro) + 2, ")");    // 插入右括号
 67             left_c--;    // 未匹配的左括号个数减1
 68         }
 69         else if (tag == 1)
 70         {
 71             tag = 0;     // 该数左边已插入"(",不可在右边插入")",但要取消标志以便下一次匹配
 72         }
 73 
 74         // 若第一循环到此处:pro是一个运算数字符串,下面再随机生成运算符并拼接
 75         // 随机生成运算符下标
 76         //srand((unsigned)time(NULL));
 77         index = rand() % 4;       // 生成运算符索引下标
 78         strcat_s(pro, strlen(pro) + 4, operators[index]);   // 拼接运算数与运算符
 79         count++;
 80     }
 81     // 此时还要再生成一个运算数并拼接
 82     op = GenerateOperand();
 83     strcat_s(pro, strlen(pro) + strlen(op) + 1, op);
 84     // 判断最后一个操作数后面是否插入“)”
 85     if (left_c)
 86     {
 87         strcat_s(pro, strlen(pro) + 2, ")");    // 插入右括号
 88         left_c--;    // 未匹配的左括号个数减1
 89     }
 90 
 91     return pro;                   // 返回生成的题目
 92 }
 93 
 94 char* GenerateOperand()    // 随机生成操作数
 95 {
 96     extern int range;      // 引用main.cpp中的全局变量range,操作数范围
 97     memset(op, 0, sizeof(op));
 98     int operand = 0;          // 随机生成的整型运算数
 99     int inteP = 0;            // 带分数的整数部分
100     int demominator = 0;      // 分母部分
101     int numerator = 0;        // 分子部分
102     // 随机生成一个数,决定要生成整数(1),真分数(2),还是带分数(3)
103     int type = 0;     // 即将生成的数据类型
104     //type = 1;
105     type = random(1, 3);
106     if (type == 1)
107     {
108         // 将生成整数
109         //srand((unsigned)time(NULL));
110         operand = rand() % range;
111         _itoa_s(operand, op, 100, 10); //把整数部分转换成字符串
112     }
113     else
114     {
115         // 先生成不为0的且在范围内的分母
116         demominator = random(2, range - 1);
117         // 生成小于分母且非0的分子
118         numerator = random(1, demominator - 1);
119         _itoa_s(numerator, op, 100, 10);    // 先把分子部分转换成字符串放入op
120         strcat_s(op, strlen(op) + 2, "/");       // 拼接分子串和"/"
121         _itoa_s(demominator, dem, 100, 10);    // 
122         strcat_s(op, strlen(op) + strlen(dem) + 1, dem);       // 拼接上分母
123         // 到此op是一个表示真分数的字符串
124         // 下面判断是否要生成分数的整数部分
125         if (type == 3)   // 将要生成带分数的整数部分
126         {
127             // 注意这个整数不能为0
128             inteP = random(1, range);        // 生成范围内且非0的整数部分
129             memset(d, 0, sizeof(d));
130             strcat_s(tmp, strlen(tmp) + strlen(op) + 1, op);       // 将分数内容移交给tmp,以便op去接收整数部分
131             //tmp = op;          // tmp寄存真分数部分
132             _itoa_s(inteP, op, 100, 10);       // 将其转换成字符串放入op
133             strcat_s(op, strlen(op) + 2, "'");       // 拼接"'"
134             strcat_s(op, strlen(op) + strlen(tmp) + 1, tmp);       // 拼接上真分数部分
135         }
136     }
137     return op;    // 返回生成的操作数
138 }

计算逆波兰表达式

#include "Calculator.h"
//#include
//#include
//#include
//#include
//#include 
//using namespace std;
time_t g_unused = (srand(time(NULL)), 0);     // Generate a seed for 'rand' in whole program.

const char* operators[4] = { " + ", " - ", " * ", " / " };

int random(int a, int b) // 生成[a,b]间的随机整数
{
    //srand((unsigned)time(NULL));
    return (rand() % (b - a + 1) + a);
}

char a[100] = "";
char* op = a;     // 存放转换成由数字转换而来的字符串
char b[100] = "";
char* pro = b;  // 存放题目
char c[100] = "";
char* dem = c;//  存放转换成字符串的分母
char d[100] = "";
char* tmp = d;;// 存放转换成字符串的带分数整数部分


char* GeneratePro()
{
    int num = 0;          // 运算符个数,1~3个
    int index = 0;        // 指示运算符的下标
    num = random(1, 3);   // 随机生成运算符个数,1到3个
    int count = 0;
    int left_c = 0;      // 未匹配的左括号个数
    int tag = 0;         // 在操作数右边插入“)”指示该操作数左边是否插入左括号
    memset(pro, 0, sizeof(pro));  // 每调用一次,都要初始化内存空间
    //memset(tmp, 0, sizeof(tmp));  // 每调用一次,都要初始化内存空间
    int way = 0;    // 括号插入方式
    while (count < num)
    {
        // 先在此处判断是否插入“(”
        if (count == 0 && num > 1 && random(0, 1))
        {
            strcat_s(pro, strlen(pro) + 2, "(");    // 插入左括号
            left_c++;    // 未匹配的左括号个数加1
            tag = 1;
        }
        else if (count == 1 && num > 1 && random(0, 1) && !left_c)
        {
            strcat_s(pro, strlen(pro) + 2, "(");    // 插入左括号
            left_c++;    // 未匹配的左括号个数加1
            tag = 1;
        }
        else if (count == 2 && num > 2 && random(0, 1) && !left_c)
        {
            strcat_s(pro, strlen(pro) + 2, "(");    // 插入左括号
            left_c++;    // 未匹配的左括号个数加1
            tag = 1;
        }
        // 随机生成一个运算数(整数或分数),并将其转变为字符串形式
        op = GenerateOperand();//operand = GenerateOperand();
        strcat_s(pro, strlen(pro) + strlen(op) + 1, op);    // 将操作符移到指向题目的指针
        // 在此处判断是否插入“)”
        if (tag != 1 && ((count == num - 1) || random(0, 1)) && left_c)
        {
            strcat_s(pro, strlen(pro) + 2, ")");    // 插入右括号
            left_c--;    // 未匹配的左括号个数减1
        }
        else if (tag == 1)
        {
            tag = 0;     // 该数左边已插入"(",不可在右边插入")",但要取消标志以便下一次匹配
        }

        // 若第一循环到此处:pro是一个运算数字符串,下面再随机生成运算符并拼接
        // 随机生成运算符下标
        //srand((unsigned)time(NULL));
        index = rand() % 4;       // 生成运算符索引下标
        strcat_s(pro, strlen(pro) + 4, operators[index]);   // 拼接运算数与运算符
        count++;
    }
    // 此时还要再生成一个运算数并拼接
    op = GenerateOperand();
    strcat_s(pro, strlen(pro) + strlen(op) + 1, op);
    // 判断最后一个操作数后面是否插入“)”
    if (left_c)
    {
        strcat_s(pro, strlen(pro) + 2, ")");    // 插入右括号
        left_c--;    // 未匹配的左括号个数减1
    }

    return pro;                   // 返回生成的题目
}

char* GenerateOperand()    // 随机生成操作数
{
    extern int range;      // 引用main.cpp中的全局变量range,操作数范围
    memset(op, 0, sizeof(op));
    int operand = 0;          // 随机生成的整型运算数
    int inteP = 0;            // 带分数的整数部分
    int demominator = 0;      // 分母部分
    int numerator = 0;        // 分子部分
    // 随机生成一个数,决定要生成整数(1),真分数(2),还是带分数(3)
    int type = 0;     // 即将生成的数据类型
    //type = 1;
    type = random(1, 3);
    if (type == 1)
    {
        // 将生成整数
        //srand((unsigned)time(NULL));
        operand = rand() % range;
        _itoa_s(operand, op, 100, 10); //把整数部分转换成字符串
    }
    else
    {
        // 先生成不为0的且在范围内的分母
        demominator = random(2, range - 1);
        // 生成小于分母且非0的分子
        numerator = random(1, demominator - 1);
        _itoa_s(numerator, op, 100, 10);    // 先把分子部分转换成字符串放入op
        strcat_s(op, strlen(op) + 2, "/");       // 拼接分子串和"/"
        _itoa_s(demominator, dem, 100, 10);    // 
        strcat_s(op, strlen(op) + strlen(dem) + 1, dem);       // 拼接上分母
        // 到此op是一个表示真分数的字符串
        // 下面判断是否要生成分数的整数部分
        if (type == 3)   // 将要生成带分数的整数部分
        {
            // 注意这个整数不能为0
            inteP = random(1, range);        // 生成范围内且非0的整数部分
            memset(d, 0, sizeof(d));
            strcat_s(tmp, strlen(tmp) + strlen(op) + 1, op);       // 将分数内容移交给tmp,以便op去接收整数部分
            //tmp = op;          // tmp寄存真分数部分
            _itoa_s(inteP, op, 100, 10);       // 将其转换成字符串放入op
            strcat_s(op, strlen(op) + 2, "'");       // 拼接"'"
            strcat_s(op, strlen(op) + strlen(tmp) + 1, tmp);       // 拼接上真分数部分
        }
    }
    return op;    // 返回生成的操作数

}

测试运行

结对作业_第6张图片


项目小结

方子茵:

 只实现了一小部分功能,因为沟通较少,不能充分了解对方的想法。没有详细计划好再编程。但是也从中意识自己能力有很大欠缺
 结对项目双方要有充分交流,明确分工,了解彼此的想法,项目才能统一起来
 结对感受:队友打码积极,但是和我一样比较拖拉,我们两个都不主动,结对项目要注重交流沟通

舒雯钰:

虽然很早就和队友沟通了想法和思路,但是比较晚开始具体编码,没有规划好时间,而且vscode的环境配置出了问题不能及时debug,我要对我的队友说抱歉QAQ。

结对感受:队友非常积极地沟通、安排进度,而且会主动说出具体的想法来对接,她负责的部分完成的很好。

 

你可能感兴趣的:(结对作业)