结对项目 实现自动生成四则运算题目的程序 (C++)
本次作业由 陈余 与 郭奕材 结对完成
零、github地址:
https://github.com/King-Authur/-Automatically-generate-four-arithmetic-problems
一、项目的相关要求
实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。
项目需求
- 使用 -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.答案1
2.答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
程序应能支持一万道题目的生成。
程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
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表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
二、遇到的困难和解决方法
(一)如何进行结对编程
在项目开始前我们进行了时长为一个多小时的讨论,理清了思路并对项目进行了整体设计和规划,随后我们分配好各自要负责的模块,并写好了接口文件进行项目的开发。
在编程的过程中,我们遇到有疑惑或者觉得思路上有出入的地方也会在微信上及时进行沟通,保证了项目的正常推进。
完成代码编写之后,我们先各自检查了自己的代码、测试模块,随后将代码整合起来,发送给对方让对方进行代码复审。在实际操作中,我们都为对方发现了许多的bug。
(二)如何存储数据
为了能够较为简易的实现对分数的存储和计算等操作,我们经过讨论,决定选择用结构体来存储,并对结构体的部分运算符进行重载。
三、关键代码 和 设计说明
整体设计
数据的定义
typedef struct variable
{
int num_or_Symbol; //0是数字1是符号
int Symbol = -1; // + - * % ( ) 分别表示为 0 1 2 3 4 5
int numer; //如果是数字此为分子
int Den = 1; //如果是数字此为分母
int num; //如果是数字此为分数前的系数
bool operator == (variable c){
return num_or_Symbol == c.num_or_Symbol && Symbol == c.Symbol && numer == c.numer && Den == c.Den && num == c.num;
}
}var;
主要的几个部分如下
//生成表达式函数
Status Create(var** exp, int size, int *length);
//计算表达式函数
Status Calculation(var* exp, int size, var* result, int length);
//中缀表达式转后缀表达式
Status Infix_to_Postfix(var* p, int size, var* Postfix, int length, int& postLen);
//判断两个问题是否等价
Status is_question_same(var* Question, int lenQuest, var* newQuestion, int lenNewQuest, int size);
//m指令的执行
void M_instructions(var** expression, int amount, int size, var* result);
//判断对错
void Correction(int* save, char* answerfile, char* exercisefile);
关键代码
创建题目
Status Create(var** exp, int size, int* length)
{
var* expre;
int mark_num = random(1, 4);//计算符个数
int pre = 0;//前括号在第pre个数字前
int aft = 0;//后括号在第aft个数字后
int judge = 0;//判断,0写入数字,1写入符号
int n = 0;
*length = mark_num + mark_num + 1;
n = 0;
if (mark_num > 1)//如果运算符有3个,则存在括号
{
pre = random(1, mark_num);
if(pre == 1)//不让括号括住整个式子
aft = random((pre + 1), (mark_num + 1));
else
aft = random((pre + 1), (mark_num + 2));
(*length) += 2;
expre = new var[*length + 1];
expre[pre * 2 - 2].num_or_Symbol = 1;
expre[pre * 2 - 2].Symbol = 4;
expre[aft * 2].num_or_Symbol = 1;
expre[aft * 2].Symbol = 5;
}
else
{
expre = new var[*length + 1];
}
n = 0;
while (n < *length)
{
if (expre[n].Symbol < 4)
{
if (judge == 0)
{
expre[n].num_or_Symbol = 0;
expre[n].Den = random(2, size);
expre[n].numer = random(0, expre[n].Den);
expre[n].num = random(1, size);
judge = 1;
}
else
{
expre[n].num_or_Symbol = 1;
expre[n].Symbol = random(0, 4);
judge = 0;
}
}
n++;
}
*exp = expre;
return SUCCESS;
}
中缀表达式转后缀表达式
Status Infix_to_Postfix(var* p, int size, var* Postfix, int length, int& postLen)
{
//传入的postfix要记得为空
var stack[maxn];
int top = 0;
for (int i = 0; i < length; i++)
{
if (p[i].num_or_Symbol == 0)//是数字
{
Postfix[postLen++] = p[i];//放入输出串中
}
if (p[i].num_or_Symbol == 1 && p[i].Symbol == 4)//左括号
{
++top;
stack[top] = p[i];
}
while (p[i].num_or_Symbol == 1 && p[i].Symbol != 4 && p[i].Symbol != 5)
{
if (top == 0 || stack[top].Symbol == 4 || prio(p[i]) > prio(stack[top]))
{
++top;
stack[top] = p[i];
break;
}
else
{
Postfix[postLen++] = stack[top];
top--;
}
}
if (p[i].num_or_Symbol == 1 && p[i].Symbol == 5)//右括号
{
while (stack[top].Symbol != 4)
{
Postfix[postLen++] = stack[top];
top--;
}
top--;
}
}
while (top != 0)
{
Postfix[postLen++] = stack[top--];
}
return SUCCESS;
}
判断题目是否等价
Status is_problem_same(var* Question, int lenQuest, var* newQuestion, int lenNewQuest, int size)
{
var Postfix1[maxn], Postfix2[maxn];
var stack1[3][3], stack2[3][3];
int len1 = 0, len2 = 0, sta_size1 = 0, sta_size2 = 0;
//获取后缀表达式
Infix_to_Postfix(Question, size, Postfix1, lenQuest , len1);
Infix_to_Postfix(newQuestion, size, Postfix2, lenNewQuest, len2);
//获取子表达式
get_Subexpression(Postfix1, len1, stack1, sta_size1);
get_Subexpression(Postfix2, len2, stack2, sta_size2);
bool flag;
for (int i = 0; i < sta_size1; i++)
{
flag = false;
for (int j = 0; j < sta_size2; j++)
{
//短式等价
if (cmp(stack1[i], stack2[j]))
{
flag = true;
stack2[j][2].Symbol = -1;//将表达式的运算符删掉
break;
}
}
if (!flag)//如果存在不一样的,返回not same
{
return ERROR;
}
}
return SUCCESS;
}
m操作
void M_instructions(var **expression, int amount, int size, var* result)
{
fstream answer;
answer.open(ANSWERFILE, ios::out | ios::app);
var results[maxn];//后缀表达式
int length;
int i = 0;
int j = 0;
int k = 0;
while (i < amount)
{
Create(&expression[i], size, &length);
result[i].Symbol = length;
if (Calculation(expression[i], size, results, length) == ERROR || results[0].num >= size || results[0].numer >= size || results[0].Den >= size)
{
continue;
}
result[i].Den = results[0].Den;
result[i].num = results[0].num;
result[i].numer = results[0].numer;
result[i].num_or_Symbol = 0;
result[i].Symbol = length;
j = 0;
while (j < i)
{
//结果一样,表达式可能一样
if (result[j].Den == result[i].Den && result[j].numer == result[i].numer && result[j].num == result[i].num)
{
if (is_question_same(expression[i], result[i].Symbol, expression[j], result[j].Symbol, size))
{
break;
}
}
j++;
}
if (i != j)
{
if(k ++ < 20)//连续20次重复答案表明给的size太小,而amount太大,表达式多样性不足
continue;
}
Visit(expression[i], length, i + 1);
answer << i + 1 << ". ";
if (result[i].numer == 0)
{
answer << result[i].num;
}
else
{
if (result[i].num != 0)
{
answer << result[i].num;
answer << "`";
}
answer << result[i].numer;
answer << "/";
answer << result[i].Den;
}
answer << endl;
i++;
k = 0;
}
answer.close();
}
四、测试运行
随机生成10道题目
改变题目数值后随机生成10道题目
测试在exercisefile.txt文件内填入答案后判断对错
五、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 10 | 10 |
·Development | ·开发 | 120 | 100 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 60 |
· Design Spec | · 生成设计文档 | 20 | 20 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 10 |
· Design | · 具体设计 | 30 | 50 |
· Coding | · 具体编码 | 180 | 200 |
· Code Review | · 代码复审 | 20 | 15 |
· Test | · 测试(自我测试,修改代码,提交修改) | 20 | 20 |
Reporting | 报告 | 30 | 30 |
· Test Report | · 测试报告 | 20 | 15 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 585 | 600 |
六、项目小结
郭奕材小结:
1、在本次项目中,我们通过结对编程,互相鼓励,相互监督,以更高的效率完成了本次任务,充分的认识到了结对编程的益处,理解它在编程效率与错误检查上的巨大作用。当然也了解到了它的缺点,尤其是在疫情期间沟通不便的客观环境下,沟通的难度也大大增加,但多亏网络会议的共享屏幕功能、微信电话的便捷、双方的耐心沟通和积极讨论等因素,让结对编程的缺点方面也得到了极大的补足。
2、在项目过程中,我学习了中缀表达式转后缀表达式的方法、计算后缀表达式的规则、判断两个题目是否等价的方法等知识,还向搭档学习了如何写更加规范的接口文件,收货颇丰。
3、另外图形化界面目前还在学习,只能做出简单的界面,未能完成项目扩展部分的需求,这是一个小小的遗憾,还需激励自己更快的学习和掌握知识,更好地完成项目需求。
4、最后再次感谢我的搭档陈余同学。
陈余小结:
1、这次项目,是我们第一次一起合作完成的项目,通过结对编程的编程方法,我们十分有效率地完成了项目需求,途中出现了不少分歧点,但通过结对编程,我们都能及时地进行讨论和统一观点,使时间没有过多地浪费在不必要的地方。
2、原本想让用户在程序里面也能填写答案,但由于想不到比较好的交互方式,所以最后没有实现这个功能,只让用户在txt文件上手动填写答案。
3、最后,和奕材带佬一起做项目真的很舒服!!
七、参考来源
[1] 波兰式、逆波兰式与表达式求值
结对项目 实现自动生成四则运算题目的程序 (C++)的更多相关文章
- 自动生成四则运算题目(C语言)
Github项目地址:https://github.com/huihuigo/expgenerator 合作者:马文辉(3118005015).卢力衔(3118005013) 项目简介 1题目:实现一 ...
- 作业二:个人编程项目——编写一个能自动生成小学四则运算题目的程序
1. 编写一个能自动生成小学四则运算题目的程序.(10分) 基本要求: 除了整数以外,还能支持真分数的四则运算. 对实现的功能进行描述,并且对实现结果要求截图. 本题发一篇随笔,内容包括: 题 ...
- Individual Project "写一个能自动生成小学四则运算题目的程序"
一.题目简介 写一个能自动生成小学四则运算题目的程序. 初步拟定要实现的功能后,估计一下自己需要花多长时间.编程过程中记录自己实际用了多长时间. 然后和同学们比较一下各自程序的功能.实现方法的异同等等 ...
- 2018-2019-2 《Java程序设计》结对项目阶段总结《四则运算——整数》(二)
20175218 2018-2019-2 <Java程序设计>结对项目阶段总结<四则运算--整数> 一.需求分析 实现一个命令行程序,要求: 自动生成小学四则运算题目(加,减, ...
- 20194651—自动生成四则运算题第一版报告chris
1.需求分析: (1)自动生成四则运算算式(+ - * /),或两则运算(+ -). (2)剔除重复算式. (3)题目数量可定制. (4)相关参数可控制. (5)生成的运算题存储到外部文件中. 2 ...
- 新版本ADT创建Android项目无法自动生成R文件解决办法
本人使用的是ADT是Version 23.0.2,支持Android 6.0之后的系统环境,最高版本23,在创建Android项目的时候,每次创建项目选择“Compile With”低于6.0版本的时 ...
- 基于c编写的关于随机生成四则运算的小程序
基于http://www.cnblogs.com/HAOZHE/p/5276763.html改编写的关于随机生成四则运算的小程序 github源码和工程文件地址:https://github.com/ ...
- C# 处理Word自动生成报告 四、程序处理
C# 处理Word自动生成报告 一.概述 C# 处理Word自动生成报告 二.数据源例子 C# 处理Word自动生成报告 三.设计模板 C# 处理Word自动生成报告 四.程序处理 现在说一下程序处理 ...
- C语言:一个能自动生成小学四则运算题目的程序
完成这个程序,半个小时内完成了,这个程序,可以自动生成小学简易的四则运算,提供菜单让用户选择,然后判断加减乘除,判断答对答错的题目个数,用户同时也可以重新选择继续答题或重新选择或退出程序. 源程序: ...
随机推荐
- Git文件合并
两个分支:主分支master,分支pre 1.将pre分支文件合并到master分支: 切换到master分支下操作: 合并文件夹[如果是文件则为a.text b.text]: git checkou ...
- acwing 173. 矩阵距离(bfs)
给定一个N行M列的01矩阵A,A[i][j] 与 A[k][l] 之间的曼哈顿距离定义为: dist(A[i][j],A[k][l])=|i−k|+|j−l|dist(A[i][j],A[k][l]) ...
- Day13_Thymeleaf简介
学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 1.Th ...
- Python unichr() 函数
描述 unichr() 函数 和 chr() 函数功能基本一样, 只不过是返回 unicode 的字符.高佣联盟 www.cgewang.com 注意: Python3 不支持 unichr(),改用 ...
- Java 将数据写入全路径下的指定文件
package com.freud.algorithm.other; import java.io.File; import java.io.FileOutputStream; public clas ...
- 最优化算法【牛顿法、拟牛顿法、BFGS算法】
一.牛顿法 对于优化函数\(f(x)\),在\(x_0\)处泰勒展开, \[f(x)=f(x_0)+f^{'}(x_0)(x-x_0)+o(\Delta x) \] 去其线性部分,忽略高阶无穷小,令\ ...
- Qt自定义控件之仪表盘2--QPaint绘制仪表盘
0.前言 前面一篇文章写道了仪表盘的特点,实现了一个贴图的仪表盘,属于低配版本的仪表盘. 主要是有任何改动时候就需要重新设计图片,不能适配不同控件大小,即使让它自由拉伸,但仪表盘放大缩小时候显示 ...
- Rest接口加Https单向认证
背景: 接到一个需求,客户要求某个模块的rest接口都得通过https访问,客户提供证书. 步骤: Server端证书生成 刚开始还没拿到客户的证书,所以通过jdk自带的keytools自己先生成了一 ...
- 实验06——java自动封箱、自动拆箱
package cn.tedu.demo; /** * @author 赵瑞鑫 E-mail:1922250303@qq.com * @version 1.0 * @创建时间:2020年7月17日 上 ...
- 东哥学Node的故事——内存管理
前言 东哥是一个平凡的前端攻城狮,北邮网研院研二在读,刚接触Node不久,心里充满了对Node的好奇和崇拜,只听噗通一声,掉入了Node的坑... 于是东哥开始疯狂地看Node相关的书籍,这不,就学到 ...