标签:10 结对 题目 String 四则运算 生成 File new
自动四则运算题目
GitHub仓库地址:https://github.com/Chavy1/arithmetic
成员:18软工4班许嘉威(3118005114) 18软工3班黄源钦(3118005052)
项目描述
该软件是一个自动生成小学四则运算题目的命令行程序,可以根据输入参数生成指定数量和数值范围的四则运算题目,还可以根据指定的路径把题目和答案生成到文件里。
项目需求
- 使用 -n 参数控制生成题目的个数
格式:MQ -n [题目数量]
例如:MQ -n 10
结果:将自动生成10个四则运算题目
要求:必含,必须指定该参数
- 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围
格式:MQ -r [数值范围]
例如:MQ -r 10
结果:生成的题目数值将在10以内,不包括10的四则运算题目。
要求:必含,必须指定该参数,否则程序报错并给出帮助信息
- 生成的题目中计算过程不能产生负数
即生成的算术表达式中如果存在形如 e1 - e2 的子表达式,那么 e1 >= e2
- 生成的题目中如果存在形如 e1 ÷ e2 的子表达式,那么其结果应是真分数
- 每道题目中出现的运算符个数不超过3个
即最长 e1 + e2 + e3 + e4
- 程序一次运行生成的题目不能重复
即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,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
- 四则运算题目2
要求:其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8
- 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件
格式:
- 答案1
- 答案2
要求:特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
- 程序应能支持一万道题目的生成
- 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计
格式: MQ -e <exercisefile>.txt -a <answerfile>.txt
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
PSP
PSP |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
|
|
|
|
5*60 |
|
Development |
开发 |
|
|
|
|
5 |
5 |
|
|
10 |
10 |
|
|
10 |
10 |
|
|
5 |
0 |
|
|
10 |
5 |
|
|
3*60 |
2*60 |
|
|
20 |
10 |
|
|
20 |
10 |
Reporting | 报告 |
|
|
|
|
30 |
15 |
|
|
5 |
5 |
|
|
20 |
20 |
|
合计 |
5*60+15 |
3*60+30 |
解题思路
该题目和上一个项目解题思路大概一直,都是像 Linux 命令参数功能,实现命令加参数功能即可实现。主要问题在于题目生成和题目检测模块。
使用的是Java语言,使用到几个固定的运算符和括号,通过Java随机类的api进行随机获取运算符和数值,在进行计算。
设计方案
使用Random类进行随机取值,对每一道题目的运算符个数,括号位置,运算数值进行生成。
利用IO类进行文件的生成以及文件的读取。获取到文件的内容进行判断。
利用接口进行核心业务代码和显示类进行交互,解耦,易于维护和修改。
代码说明
主程序入口启动类
@SpringBootApplication public class ArithmeticApplication implements CommandLineRunner { @Resource private MainPage mainPage; @Override public void run(String... args) throws Exception {
//主要显示类展示方法 mainPage.show(); } public static void main(String[] args) { SpringApplication.run(ArithmeticApplication.class, args); } }
生成题目 Generator类
/** * 生成问题列表 * @param totalNum 问题个数 * @param range 数字范围 * @return 问题列表 */ public List<Expression> generate(int totalNum, int range){ List<Expression> expressionList = new LinkedList<>(); int i = 0; while (i < totalNum){ Expression expression = new Expression(); int totalOperator = randInt(1,3); List<Number> numberList = generateNumList(totalOperator+1,range); List<String> optList = generateOptList(totalOperator); List<Position> positionList = generateBracketsPos(totalOperator); expression.setTotalOperator(totalOperator); expression.setNumberList(numberList); expression.setOperatorList(optList); expression.setBracketsPos(positionList); //产生的表达式有问题 if(expression.check()){ continue; } if(isRepeat(expressionList,expression)){ continue; } expressionList.add(expression); i++; } return expressionList; }
生成题目文件和文件检测 GeneratorFile类
/** * 加题目和答案输出到指定路径 * @param expressionList 题目列表 * @param questionPath 问题文件的路径 * @param answerPath 答案文件的路径 */ public void generatorToFile(List<Expression> expressionList, String questionPath, String answerPath){ File questionFile = new File(questionPath); File answerFile = new File(answerPath); BufferedWriter questionWriter = null; BufferedWriter answerWriter = null; try { questionWriter = new BufferedWriter(new FileWriter(questionFile)); answerWriter = new BufferedWriter(new FileWriter(answerFile)); for(int i = 0; i<expressionList.size();i++){ questionWriter.write((i+1)+". "+expressionList.get(i).printAsQuestion()); questionWriter.newLine(); questionWriter.flush(); answerWriter.write((i+1)+". "+expressionList.get(i).getAnswer().toString()); answerWriter.newLine(); answerWriter.flush(); } } catch (IOException e) { e.printStackTrace(); } try { answerWriter.close(); questionWriter.close(); } catch (IOException e) { e.printStackTrace(); } }
/** * 判断答案是否正确并输出到文件 * @param questionPath 已经有答案的问题文件 * @param answerPath 答案文件 * @param judgePath 结果 */ public void judgeFile(String questionPath, String answerPath, String judgePath){ File questionFile = new File(questionPath); File answerFile = new File(answerPath); File judgeFile = new File(judgePath); if(!questionFile.exists()||!answerFile.exists()){ throw new RuntimeException("文件路径错误"); } BufferedReader questionReader = null; BufferedReader answerReader = null; BufferedWriter judgeWriter = null; try {
//文件IO操作 questionReader = new BufferedReader(new FileReader(questionFile)); answerReader = new BufferedReader(new FileReader(answerFile)); judgeWriter = new BufferedWriter(new FileWriter(judgeFile)); String questionLine; String answerLine; String yourAnswer; String questionCount; String [] answerStrings; List<String> correctList = new LinkedList<>(); List<String> wrongList = new LinkedList<>();
//对文件内容进行处理,解析题目和答案 while ((questionLine = questionReader.readLine())!=null){ int begin = questionLine.indexOf('='); yourAnswer = questionLine.substring(begin+1); yourAnswer = yourAnswer.trim(); int countEnd = questionLine.indexOf('.'); questionCount = questionLine.substring(0,countEnd); questionCount = questionCount.trim(); answerLine = answerReader.readLine(); answerLine = answerLine.trim(); answerStrings = answerLine.split(" "); if(yourAnswer.equals(answerStrings[answerStrings.length-1])){ correctList.add(questionCount); }else { wrongList.add(questionCount); } } StringBuilder correctStringBuilder = new StringBuilder(); correctStringBuilder.append("Correct:"+correctList.size()+"( "); for(String s : correctList){ correctStringBuilder.append(s); correctStringBuilder.append(", "); } int index = correctStringBuilder.lastIndexOf(","); if(index == -1){ correctStringBuilder.append(")"); }else { correctStringBuilder.replace(index,correctStringBuilder.length(),")"); } StringBuilder wrongStringBuilder = new StringBuilder(); wrongStringBuilder.append("Wrong:"+wrongList.size()+"( "); for(String s : wrongList){ wrongStringBuilder.append(s); wrongStringBuilder.append(", "); } index = wrongStringBuilder.lastIndexOf(","); if(index == -1){ wrongStringBuilder.append(")"); }else { wrongStringBuilder.replace(index,wrongStringBuilder.length(),")"); } judgeWriter.write(correctStringBuilder.toString()); judgeWriter.newLine(); judgeWriter.write(wrongStringBuilder.toString()); judgeWriter.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); } try { judgeWriter.close(); answerReader.close(); questionReader.close(); } catch (IOException e) { e.printStackTrace(); } }
测试运行
单元测试
本项目使用了 Junit5 来实现单元测试,主要业务类在 core 包下,因此只需要对 core 包下的具体接口编写单测类
对单测类使用 Junit5 进行覆盖率运行分析,结果如下:
core 包的单元测试运行结果达到方法覆盖率 96% ,类覆盖率 100% , 行覆盖率 81%
运行测试
- 测试 -n -r 参数
测试用例: MQ -n 20 -r 20
运行结果:
- 测试 -e -a 参数
测试用例:MQ -e C:\\Users\\HP\\Desktop\\Exercises.txt -a C:\\Users\\HP\\Desktop\\Answers.txt
预期结果:正确 9 错误 1
运行结果:
总结
在做结对编程项目的时候,主要的流程就是两个人先各自了解题目,在一起讨论整个项目的需求,讨论设计方案,讨论代码规范。再根据需求和模块进行分工,规定好两个人对接的接口,然后在Github上进行协同合作编码。
因此在整个流程里,除去原来做项目的流程,沟通和规定约束是非常重要的一部分,不仅仅是结对编程中,团队中一个项目的开展都需要经过无数次开会,规定规范,约束,讨论,达成一致,才能更好的进行开工。
标签:10,结对,题目,String,四则运算,生成,File,new 来源: https://www.cnblogs.com/Chavy/p/12591147.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。