码云地址:https://git.oschina.net/lzx84/szys
题目:
从《构建之法》第一章的 “程序” 例子出发,像阿超那样,花二十分钟写一个能自动生成小学四则运算题目的命令行 “软件”。
满足以下需求:
- 除了整数以外,还要支持真分数的四则运算,真分数的运算,例如:1/6 + 1/8 = 7/24
- 运算符为 +, −, ×, ÷
- 并且要求能处理用户的输入,并判断对错,打分统计正确率。
- 要求能处理用户输入的真分数, 如 1/2, 5/12 等
-
使用 -n 参数控制生成题目的个数,例如执行下面命令将生成10个题目
Myapp.exe -n 10
需求分析:
- 需要处理分数,包括化简分数、识别整数等。并考虑特殊情况,如分数分母不为0等。考虑到题目要求小学水平,则分子分母数值不超过100。
- 需要编写分数四则运算的算法,并考虑特殊情况,如减法结果不能为负数,除法中除数不能为0等。
- 需统计用户正确的题目数,与总题目数。
- 需要实现字符串的识别与比较。
功能实现:
- 基本功能:能随机生成若干四则运算式,并检测用户答案,统计正确率。
- 扩展功能:实现多项运算、统计做题时长、避免重复题型、控制题目难度等(未实现)
- 高级功能:实现错题统计功能、实现多用户竞赛功能、实现更复杂的运算等(未实现)
设计实现:
Fraction类:
Calculate类:
Dofrac类:
代码说明:
Fraction类:
public class Fraction {
private int fenzi;
private int fenmu;
public Fraction() {
super();
}
public Fraction(int fenzi, int fenmu) {
super();
this.fenzi = fenzi;
this.fenmu = fenmu;
this.simplify(fenzi, fenmu); //在构造函数中直接化简分数(感觉此方法欠妥)
}
public void simplify(int fenzi, int fenmu) {
int GCD = Dofrac.GCD(fenzi, fenmu);
this.fenzi = fenzi / GCD;
this.fenmu = fenmu / GCD;
}//化简分数
@Override
public String toString() {
if (fenzi == 0) {
return 0 + "";
} else if (fenzi % fenmu == 0) {
return fenzi / fenmu + "";
} else
return fenzi + "/" + fenmu;
}//改写toString,输出为分数形式
public int getFenzi() {
return fenzi;
}
public void setFenzi(int fenzi) {
this.fenzi = fenzi;
}
public int getFenmu() {
return fenmu;
}
public void setFenmu(int fenmu) {
this.fenmu = fenmu;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + fenmu;
result = prime * result + fenzi;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Fraction other = (Fraction) obj;
if (fenmu != other.fenmu)
return false;
if (fenzi != other.fenzi)
return false;
return true;
}
}
Calculate类:
public class Calculate {
public static Fraction add(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fm2 + fm1 * fz2, fm1 * fm2);
return f;
}//加法运算
public static Fraction sub(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fm2 - fm1 * fz2, fm1 * fm2);
return f;
}//减法运算
public static Fraction mul(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fz2, fm1 * fm2);
return f;
}//乘法运算
public static Fraction div(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fm2, fm1 * fz2);
return f;
}//除法运算
public static boolean compare(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
if (fz1 * fm2 >= fz2 * fm1) {
return true;
} else{
return false;
}
}//比较两分数的大小
}
Dofrac类:
public class Dofrac {
public static Fraction CreatFrac() {
int fz, fm,co;
co=(int)(Math.random()*2);//co取[0,1]得随机值,分别代表分数和整数
if(co==0){
fm = (int) (2 + Math.random() * (100 - 2 + 1));//分母为2-100的随机数
fz =(int) (0 + Math.random() * (fm - 0 + 1));//分子为 0--fm 的随机数(确保真分数)
}else {
fm=1;
fz=(int) (0 + Math.random() * (100 - 0 + 1));
}
Fraction frac = new Fraction(fz, fm);
return frac;
}//创建随机分数
public static boolean check(String input) {
if (input.matches("[0-9]+")||input.matches("[0-9]+/[0-9]+")) {
return true;
} else
{
return false;
}
}//利用正则表达式处理用户输入的答案
public static int GCD(int m, int n) {
while (true) {
if ((m = m % n) == 0)
return n;
if ((n = n % m) == 0)
return m;
}
}//计算最大公约数
public static Fraction MakeFor(Fraction f1, Fraction f2, int op, int i) {
Fraction result = new Fraction();
switch (op) {
case 1: {
result = Calculate.add(f1, f2);
System.out.println("第" + i + "题:" + f1.toString() + "+" + f2.toString());
break;
}
case 2: {
if (!Calculate.compare(f1, f2)) {
Fraction temp = f1;
f1 = f2;
f2 = temp;
}//比较两分数的大小,若减数小于被减数,则将两个数交换
result = Calculate.sub(f1, f2);
System.out.println("第" + i + "题:" + f1.toString() + "-" + f2.toString());
break;
}
case 3: {
result = Calculate.mul(f1, f2);
System.out.println("第" + i + "题:" + f1.toString() + "*" + f2.toString());
break;
}
case 4: {
if (f2.getFenzi() == 0) {
f2.setFenzi((int) (1 + Math.random() * (10 - 1 + 1)));
}//若除数分子为0,则分子重新取一个1——100的随机数
result = Calculate.div(f1, f2);
System.out.println("第" + i + "题:" + f1.toString() + "÷" + f2.toString());
break;
}
}
return result;
}
}
Generator类(执行类)
public class Generator {
public static void main(String[] args) {
try {
int op = 0;
String input;
int flag = 0;//用于检测用户答对的题目数
Scanner sc = new Scanner(System.in);
System.out.println("【四则运算题目生成器】");
System.out.println("请输入需要的题目数量:");
int n = sc.nextInt();
for (int i = 1; i <= n; i++) {
Fraction f1 = Dofrac.CreatFrac();
Fraction f2 = Dofrac.CreatFrac();
op = (int) (Math.random() * 4 + 1);
Fraction result = Dofrac.MakeFor(f1, f2, op, i);
input = sc.next();
while (!Dofrac.check(input)) {
System.out.println("输入有误,请重新输入:");
input = sc.next();
}
if (input.equals(result.toString())) {
System.out.println("回答正确");
System.out.println("---------------------------------------------");
flag++;
} else {
System.out.println("回答错误,正确答案是:" + result.toString());
System.out.println("---------------------------------------------");
}
}
System.out.println("---------------------------------------------");
System.out.println("答题完毕,你的正确率为" + 100 * flag / n + "%");
sc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试运行:
PSP表:
PSP2.1 | Personal Software Process Stages | Time (%) Senior Student | Time (%) |
Planning | 计划 | 5min | 30min |
· Estimate | 估计这个任务需要多少时间 | 8min | 10min |
Development | 开发 | 2h | 1h30min |
· Analysis | 需求分析 (包括学习新技术) | ||
· Design Spec | 生成设计文档 | ||
· Design Review | 设计复审 | 3min | 1min |
· Coding Standard | 代码规范 | ||
· Design | 具体设计 | 1h | |
· Coding | 具体编码 | 40min | |
· Code Review | 代码复审 | ||
· Test | 测试(自我测试,修改代码,提交修改) | ||
Reporting | 报告 | ||
· | 测试报告 | ||
计算工作量 | |||
并提出过程改进计划 |
总结:
此次作业整体难度不大,但是刚开始看到题目构思的时候,却一度陷入僵局。也许是因为太久没有编程,很多知识都忘记了,导致刚开始的时候不懂从何下手。在思考了很久,复习了很多以前的知识后,大体上有了思路,但也不是很清晰,想到什么写什么,迷迷糊糊就完成了。中间遇到的BUG也不是很多,但是调试花了很多时间,感觉这是自己编程思想不够系统,对软件的应用也不是很熟悉造成的,根本原因还是在于练习不够,经验不足。希望以后多加练习,可以更加进步。最后感谢老师们和助教的辛勤付出,你们辛苦了!