so easy, too happy
一、预估与实际
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 10 | 8 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 30 | 20 |
• Design Spec | • 生成设计文档 | 30 | 30 |
• Design Review | • 设计复审 | 10 | 15 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
• Design | • 具体设计 | 25 | 20 |
• Coding | • 具体编码 | 200 | 120 |
• Code Review | • 代码复审 | 20 | 25 |
• Test | • 测试(自我测试,修改代码,提交修改) | 120 | 110 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 40 | 50 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 50 |
合计 | 468 |
二、需求分析
我通过百度查看一,二年数学教学大纲的方式了解到,小学一年级数学有如下的几个特点:
- 特点1:
- 都是一百以内加减法
- 特点2:
- 都是整数
- 特点3:
- 不会出现负数
- 特点3:
- 一年级只能是两位数加、减整十数和两位数加、减一位数
- 特点4:
- 二年级加法是两位数加减两位数
- 特点4:
- 二年级乘数法只能是表内乘法和表内除法
经过分析,我认为,这个程序应当:
- 使用的数字大于0,小于100
- 数字用int类型储存
- 涉及减法时要注意相减不会出现负数
- 对于main函数参数数组的长度判断,避免越界
- 使用两个可变字符串储存将要输出到文件中的字符串
三、设计
1. 设计思路
- 程序有一个类,四个方法,主要涉及到三种业务:
- 对输入的参数进行判断,保证参数的合法性
- 随机生成两个100以内的正整数,和运算符号并进行运算
- 将生成的题目和答案写入out.txt中
- 算法的关键:
- 判断输入的参数是否合法,对于不合法的各种情况需给予提示
- 生成的数字是否满足大纲要求时的判断逻辑
2. 实现方案
- 准备工作:先在Github上创建仓库,克隆到本地,编写代码后通过git上传到Github上
- 技术关键点:
- 在判断用户输入时难点在于判断输入的数字不能过大,一年几的题目都是100以内,两个数组最多不超过10000种(如果考虑相减后无负数则会更少),命令行运行程序输入的参数储存在字符串中,所以可以通过判断字符串的长度不超过4,还有一个注意点对于输入0000001这中参数应该先将字符串的前导0去除后再判断
- java中通过Math中random()方法生成一个0~1的小数,然后乘100取整得到需要的数字
- 在生成随机数后需要针对加减乘除四种情况处理生成的数字,使之符合大纲要求
- 加法:当第一个数大于0时,第二个数只能是小于10或者大于0且膜10等于0
- 减法:先判断第一个和第二个数的大小,确保第一个数大于第二个数,再判断当第一个数大于0且第二个数也大于10时,第二个数等于它自身除10再乘10,以确保第二个数是一个整十数
- 乘法:两个数都必须小于10
- 除法:当第一个数是两位数时,第二个数必须小于10
- 每次生成的题目和答案可以分别保存到两个StringBuffer对象中,然后通过字符流输入到文件中,注意保证文件路径必须存在,不存在的话需要创建路径
四、编码
- 设计思路:
- 先通过scanner函数接收用户的输入来进行调试,最后再改为使用main函数的参数。
- 通过正则匹配输入的参数是否全为数字,然后检验数字的位数。
- 通过Math.random()生成01的随机数,乘101再取整就可以生成0100的随机数
- 生成的随机数%2得到0或1来代表 + 或 -
- 确保生成的数符合大纲要求
- 因为标准答案在所有题目之后,并且也包含题目,不能直接生成一道题就写入一道在文件中,所以采用两个StringBuffer对象分别保存,最后统一输入文件
- 对于文件输入先判断文件路径是否存在,不存在要创建路径,再向文件中输入数据
- 遇到的问题与解决方案:
- 一开始直接检验位数是否大于9,后来经过讨论发现所以的题目中数不会超过10000,并且要先去除字符串的前导0,还有因为是通过正则去匹配是否全为数字,所以不需要再去额外判断是否为负数
- 如果直接使用生成的随机数进行运算,相减会出现负数,后来加了一个判断,确保第一个数比第二个数大,如果不大就交换两个数
- 在解决一年级减法中第二个数是整十或者一位数时,写好了逻辑判断但依然有吧符合数据的情况出现,原因在于之后又进行了交换两数,解决方案是先保证第一个数比第二个数大再去处理第二个数为整十(通过除10再乘10)
- 在存入数据时,一开时想每生成一个数据存入一次,但这样就无法按照要求将标准答案输入到所有题目之后,后来使用两个StringBuffer对象先将题目和答案储存起来,最后同一输出
- 一开始换行符使用\n 对于输出的文件用记事本打开没有换行,用编辑器打开才能看到换行,这样用户体验不好,后来改用System.lineSeparator()函数调用当前系统的换行符进行换行
1. 调试日志
- 有几率出现java.lang.ArithmeticException / by zero 除零异常, 原因在于除法没有判断除数是否为0
- 有几率出现死循环,原因是每次当生成的数字不符合大纲要求时就重新生成随机数,不能确保在有限时间内获得的想要的数字,解决方案只在每次生成新题目开始调用一次随机数的生成,在计算结果时争对加减乘除分别判断数字是否符合大纲要求,对于不符合的数字通过 / , % , *等运算进行处理达到要求不用再次循环生成随机数
2. 关键代码
/**
* 作用:生成题目
* @param len 用户要求生成的题目数量
* @param grade 年级
*/
private static void generatingTopic(int len,int grade) {
for (int i = 1; i <= len; i++) {
// 获取两个随机数,num1,num2表示参与计算的两个数字;
int num1 = (int) (Math.random() * 100);
int num2 = (int) (Math.random() * 100);
// symbol代表运算符号;
int index = (1 == grade) ? ((int) (Math.random() * 10)) % 2 : ((int) (Math.random() * 10)) % 4;
String symbol = Operator[index];
//确保加法时和不超过100
while(0 == index && num1 + num2 >= 100) {
num1 = (int) (Math.random() * 100);
num2 = (int) (Math.random() * 100);
}
// 计算结果
int res = 0;
int remainder = 0; // 余数
switch (symbol) {
case " + ":
// 确保小学1年级题目为两位数加减整十数和两位数加减一位数
if(1 == grade && num1>10 && num2 >10 && num1%10 != 0 && num2%10 !=0) {
num2 = num2/10*10;
}
res = num1 + num2;
break;
case " - ":
// 确保第一个数比第二个数大,避免相减出现负数,小学加减无负数
if (num1 < num2) {
int temp = num1;
num1 = num2;
num2 = temp;
}
// 确保小学1年级题目为两位数加减整十数和两位数加减一位数
if(1 == grade && num1>10 && num2 >10 && num2%10 !=0) {
num2 = num2/10*10;
}
res = num1 - num2;
break;
case " * ":
// 确保为表内乘法(两个数都为一位数)
num1 %= 10;
num2 %= 10;
res = num1 * num2;
break;
case " / ":
// 防止除数为0
while(0 == num2) {
num2 = (int) (Math.random() * 100);
}
// 确保为表内除法(除数只能是一位数,不能使用%,因为又可能出现%的结果为0)
if(num2>10) {
num2 /=10 ;
}
res = num1 / num2;
remainder = num1 % num2;
break;
}
// 将题目和答案记录到可变字符串中
topic.append("(" + i + ") " + num1 + symbol + num2 + System.lineSeparator());
if (symbol.equals(" / ")) {
standAnswer.append("(" + i + ") " + num1 + symbol + num2 + " = " + res
+ (remainder != 0 ? ("..." + remainder) : "") + System.lineSeparator());
} else {
standAnswer.append("(" + i + ") " + num1 + symbol + num2 + " = " + res + System.lineSeparator());
}
}
}
3. 代码规范
- 第一条:类名使用UpperCamelCase风格,但是以下情形例外:DO / BO / DTO / VO / AO / PO 等。
- 第二条:方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵循驼峰形式。
- 第三条:大括号的使用约定。如果是大括号内为空,则简介地写成{}即可,不需要换行;如果是非空代码块则:
- 左大括号前不换行。
- 左大括号后换行。
- 右大括号前换行。
- 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。
- 第四条:左小括号和字符之间不出现空格;同样的,有小括号和字符之间也不出现空格。详见第5条下面正例提示。
反例:if (空格 a == b 空格) - 第五条: if/for/while/switch/do等保留字与括号之间都必须加空格。
- 第六条:任何二目、三木运算符的左右两边都需要加一个空格。 说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。
- 第七条:不同逻辑、不同语义、不同业务的代码之间插入一个空行分割开来以提升可读性。
- 第八条:在一个switch块内,每一个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使空代码。
五、测试
测试 | 预期结果 | 实际结果 |
---|---|---|
java MathExam6313 | 运行程序时请输入两个数字代表要生成的题数和年级。 | 运行程序时请输入两个数字代表要生成的题数和年级。 |
java MathExam6313 10 | 小学1年级数学题题目已生成,请查看out.txt文件 | 小学1年级数学题题目已生成,请查看out.txt文件 |
java MathExam6313 10 2 | 小学2年级数学题题目已生成,请查看out.txt文件 | 小学2年级数学题题目已生成,请查看out.txt文件 |
java MathExam6313 10 1 | 小学1年级数学题题目已生成,请查看out.txt文件 | 小学1年级数学题题目已生成,请查看out.txt文件 |
java MathExam6313 000000000001 1 | 小学1年级数学题题目已生成,请查看out.txt文件 | 小学1年级数学题题目已生成,请查看out.txt文件 |
java MathExam6313 000000000001 2 | 小学2年级数学题题目已生成,请查看out.txt文件 | 小学2年级数学题题目已生成,请查看out.txt文件 |
java MathExam6313 10 3 | 输入的第二个参数不是1或2,请重新运行,第二个参数输入1或2 | 输入的第二个参数不是1或2,请重新运行,第二个参数输入1或2 |
java MathExam6313 100 1 1 | 最多输入两个个数字参数,第一个代表题目数量,第二个代表年级 | 最多输入两个个数字参数,第一个代表题目数量,第二个代表年级 |
java MathExam6313 cbsckj 1 | 输入的第一个参数不是正整数,请重新运行,输入一个正整数 | 输入的第一个参数不是正整数,请重新运行,输入一个正整数 |
java MathExam6313 10 dsv | 输入的第二个参数不是1或2,请重新运行,第二个参数输入1或2 | 输入的第二个参数不是1或2,请重新运行,第二个参数输入1或2 |
java MathExam6313 9999999999999999 | 第一个参数数字过大,请重新运行,输入参数 | 第一个参数数字过大,请重新运行,输入参数 |
六、总结
- 一开始的时候只是想了一下主要的流程,然后直接在main函数中直接敲代码,最后让整个程序显得又臭又长,拥挤不堪.尤其在调试程序修改代码时,让人头大。后来重构代码,将整个业务逻辑分为三个功能,每个功能一个函数,整个代码立马变得清晰起来。
- 一定要先写注释,再写代码,每一个代码块之间的结构要明确、清晰
- 一定要先写注释,再写代码,每一个代码块之间的结构要明确、清晰
- 一定要先写注释,再写代码,每一个代码块之间的结构要明确、清晰
- 我自己写代码时是会先写注释再写代码,但是在帮其他同学解决问题时发现,基本数大家都不写注释,或者只有零零散散的一点注释,代码块之间的缩进也是混乱的,光看代码就要看好久,才明白他写的是什么,找bug就更难了,代码写清晰了,与人方便,于己方便,先写注释还有一个好处就是帮助自己理清逻辑,当注释写好了,逻辑清楚了,剩下的就像填空题一样了,so easy, too happy
- 还有就是需求分析要弄好,不然代码敲了半天,最后还要反工,我做这道题就是一开始相当然的认为一年级的题目就只是100以内加减法而已,但后来仔细阅读了教学大纲和练习册才发现还有一些其他要求,这时候看着长长的代码欲哭无泪,又得吭哧吭哧改代码
- 每写一个功能就测试一下,这样也可以减少后期bug修改的次数,不然写了半天运行结果发现前面某行代码出错,找bug都要找好久
so easy, too happy的更多相关文章
- 【转】Windows下使用libsvm中的grid.py和easy.py进行参数调优
libsvm中有进行参数调优的工具grid.py和easy.py可以使用,这些工具可以帮助我们选择更好的参数,减少自己参数选优带来的烦扰. 所需工具:libsvm.gnuplot 本机环境:Windo ...
- Struts2 easy UI插件
一.easy UI是类似于jQuery UI的插件库,它提供了丰富的各种常用插件:tree.datagrid... tree插件: 语法:$(selector).tree([settings]); 常 ...
- Easy UI常用插件使用
一.easy UI是类似于jQuery UI的插件库,它提供了丰富的各种常用插件:tree.datagrid... tree插件: 语法:$(selector).tree([settings]); 常 ...
- UVA-11991 Easy Problem from Rujia Liu?
Problem E Easy Problem from Rujia Liu? Though Rujia Liu usually sets hard problems for contests (for ...
- CodeForces462 A. Appleman and Easy Task
A. Appleman and Easy Task time limit per test 1 second memory limit per test 256 megabytes input sta ...
- easy ui插件
简介: easy UI是类似于jQuery UI的插件库 注意:多脚本同时使用时,注意脚本冲突问题. 常用插件: 1.tree插件(tree插件实现动态树形菜单) 2.datagrid插件(datag ...
- 用TPP开启TDD的easy模式
Test-Drived Development 测试驱动开发三步曲:写一个失败的测试用例->编写生产代码通过这个测试用例(transformation)->重构(refactor).重构是 ...
- Easy Sysprep更新日志-skyfree大神
Easy Sysprep更新日志: Skyfree 发表于 2016-1-22 13:55:55 https://www.itsk.com/forum.php?mod=viewthread&t ...
- [官方软件] Easy Sysprep v4.3.29.602 【系统封装部署利器】(2016.01.22)--skyfree大神
[官方软件] Easy Sysprep v4.3.29.602 [系统封装部署利器](2016.01.22) Skyfree 发表于 2016-1-22 13:55:55 https://www.it ...
- [原创] Easy SysLite V1.2 (2016.5.29更新,新增加WIN10支持,一个程序适配所有系统减肥)
[原创] Easy SysLite V1.2 (2016.5.29更新,新增加WIN10支持,一个程序适配所有系统减肥) nohacks 发表于 2016-5-29 17:12:51 https:// ...
随机推荐
- Jmeter新手频犯错误之一(登录)
昨天被人问了一个问题:为什么我用Jmeter先创建一个登录请求,然后创建一个操作(比如计算账单)请求,运行之后结果树中却是status_code=401(即登录失败),我明明登录了啊.... emmm ...
- linux如何查看进程OOM killer
基本概念: Linux 内核有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程 ...
- iOS 多线程:『GCD』详尽总结
本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法.这大概是史上最详细.清晰的关于 GCD 的详细讲解+总结的文章了.通过本文,您将了解到: 1. GCD 简介 2. GCD 任务和队列 ...
- ruby puts, print, p方法比较
1.puts([obj[, obj2[, ....]]] ) 依次将obj和换行符输出到$>.若没有参数的话则只会输出换行符. 若参数是数组,则依次输出数组元素和换行符.若将既非数组又非字符串的 ...
- mimikatz将结果输出到一个文本的命令
mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" > pssword.txt
- go学习笔记-环境安装
环境安装 环境安装 主要包含三个部分 运行环境及开发sdk 系统环境和路径配置 IDE配置 以mac环境为例,其他环境类似 运行环境及开发sdk 使用 brew 安装 brew install go ...
- JS 判断滚动底部并加载更多效果。。。。。。。。。
JS 判断滚动底部并加载更多效果......... <html lang="zh-cn"> <head> <meta http-equiv=" ...
- 深度学习与计算机视觉(11)_基于deep learning的快速图像检索系统
深度学习与计算机视觉(11)_基于deep learning的快速图像检索系统 作者:寒小阳 时间:2016年3月. 出处:http://blog.csdn.net/han_xiaoyang/arti ...
- 【转载】COM 组件设计与应用(十一)—— IDispatch 及双接口的调用
原文:http://vckbase.com/index.php/wv/1236.html 一.前言 前段时间,由于工作比较忙,没有能及时地写作.其间收到了很多网友的来信询问和鼓励,在此一并表示感谢.咳 ...
- Quartz动态添加,修改,删除任务(暂停,任务状态,恢复,最近触发时间)
首页 博客 学院 下载 图文课 论坛 APP 问答 商城 VIP会员 活动 招聘 ITeye GitChat 写博客 小程序 消息 登录注册 关闭 quartz_Cron表达式一分钟教程 09-05 ...