第一部分:要求
1.作业需求:任何编程语言都可以,命令行程序接受一个数字输入,然后输出相应数目的四则运算题目和答案。例如输入数字是 30, 那就输出 30 道题目和答案。 运算式子必须至少有两个运算符,运算数字是在 100 之内的正整数,答案不能是负数。
2.拓展要求:
1) 要求能出和真分数 (二分之一, 十二分之五,等)相关的练习题。
2) 并且要求能处理用户的输入,并判断对错,打分统计。 要求能处理用户输入的真分数, 如 1/2, 5/12 等。
初步拟定要实现的功能后,估计一下自己需要花多长时间。编程过程中记录自己实际用了多长时间。
然后和同学们比较一下各自程序的功能、实现方法的异同等等。
第二部分:设计
1.需求分析
(1)随机产生 100 之内的正整数或者真分数的题目。
(2)答案不能是负数。
(3)题目数根据用户而定。
(4)运算式子必须至少有两个运算符。
. (5)判断用户输入值的对错,并给出用户的正确题目数和分数。
2.功能设计
1.根据用户要求的题目数,循环num次输出随机四则运算。
2.判断用户输入值是否正确,并给出正确题目数。
3.答案不能小于0,小于0就重新输出题目。
4.能出和真分数 (二分之一, 十二分之五,等)相关的练习题。
3.代码规范
命名规范:使用匈牙利命名法
程序风格:程序按照阶梯式编写,括号习惯使用其一个括号在语句后面,结尾括号另占一行
注释规范:函数头的注释(“功能”,“返回值”),在一些关键的语句上面进行注释
第三部分:代码实现
总体设计:
◻随机产生100以内的正整数,运算符号以及正整数和运算符号的数量,通过调参可以调整生成正整数和运算符号的数量,四则运算乘除和加减优先级别:先乘除后加减,在计算函数中遍历运算符数组中乘除符号后进行优先计算,之后再对乘除计算之后的运算数进行相加减运算。
◻真分数设计:随机生成真分数,分子与分母大小范围在1-20随机生成,范围可以通过改变参数进行调整,分数约分后用对象进行存储。可以通过改变参数对分数运算数的数量(计算难度较大,这里使用生成两个运算数进行运算)。分数运算时,优先乘除后再相加减,会对最终计算结果进行约分到最简。
◻当随机产生的正整数运算题和真分数运算题计算结果为负数则递归重新生成运算题,直至结果是大于零时产生题目并打印在控制台上,用户输入答案,答案与结果匹配正确即统计正确题数,错误的输出打印正确结果,当全部运算题做完后输出统计结果以及分数。分数计算公式: 100 * (正确数 / 总题数)。
主要代码:
// 生成运算公式并计算正确结果,将最终的存入List中
public void generateFormula() {
Random random = new Random();
int operationNum = random.nextInt(3) + 3; //运算数的个数
int operatorNum = operationNum - 1; //运算符的个数
List<String> newOperators;
List<Integer> newOperations;
int newAnswer;
//计算运算式答案
do {
List<String> operators = storeOpInList(operatorNum);
List<Integer> operations = operations(operationNum, operators);
newOperators = deepCopy(operators);
newOperations = deepCopy(operations);
newAnswer = generateCorrectAnswer(operators, operations);
} while (newAnswer < 0);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < operationNum - 1; i++)
stringBuilder.append(newOperations.get(i) + " " + newOperators.get(i) + " ");
stringBuilder.append(newOperations.get(operationNum - 1) + " = ");
System.out.println(stringBuilder.toString());
operatorExpressionList.add(stringBuilder.toString());
correctAnswerList.add(String.valueOf(newAnswer));
}
/**
* @author: 梁铭标
* @Date:2018.10.10
* @Content:可序列化真分数对象
*/
public class Fraction implements Serializable {
private static final long serialVersionUID = -3245478690496182643L;
private int numerator; //分子
private int denominator; //分母
public Fraction(int numerator, int denominator) {
this.numerator = numerator;
this.denominator = denominator;
}
public int getNumerator() {
return numerator;
}
public int getDenominator() {
return denominator;
}
public void setNumerator(int numerator) {
this.numerator = numerator;
}
public void setDenominator(int denominator) {
this.denominator = denominator;
}
}
计算真分数运算式的正确结果
1 // 计算真分数运算式的正确结果
2 public Fraction generateCorrectAnswer(List<String> operators, List<Fraction> operations) {
3 //遍历运算符容器,完成乘除运算
4 for (int i = 0; i < operators.size(); i++) {
5 String operator = operators.get(i);
6 if (operator.equals("*") || operator.equals("/")) {
7 operators.remove(i); //乘除符号将其从集合中移除
8 Fraction fractionLeft = operations.remove(i); //拿运算符左侧的数字
9 Fraction fractionRight = operations.remove(i); //拿运算符右侧的数字
10 if (operator.equals("*"))
11 operations.add(fractionMultiple(fractionLeft, fractionRight));
12 else
13 operations.add(fractionDivide(fractionLeft, fractionRight));
14 i--; //运算符容器的指针回到原来的位置,防止跳过下一个运算符
15 }
16 }
17
18 //遍历运算符容器,完成加减运算,当运算符容器为空时,运算结束
19 while (!operators.isEmpty()) {
20 String operator = operators.remove(0);
21 Fraction fractionLeft = operations.remove(0);
22 Fraction fractionRight = operations.remove(0);
23 if (operator.equals("+"))
24 fractionLeft = fractionAdd(fractionLeft, fractionRight);
25 else
26 fractionLeft = fractionSubtract(fractionLeft, fractionRight);
27 operations.add(0, fractionLeft);
28 }
29
30 //返回计算结果
31 return operations.get(0);
32 }
该作业的程序总引擎
/**
* @author: 梁铭标
* @Date:2018.10.10
* @Content:正整数和真分数总引擎
*/
public class ExecutionEengine {
public static void main(String[] args) {
SimpleFourOperation simpleFourOperation = new SimpleFourOperation();
simpleFourOperation.init();
FractionOperation fractionOperation = new FractionOperation();
Scanner scanner = new Scanner(System.in);
System.out.print("请输入生成四则运算题目的个数:");
int titleNum = handleUserInput(scanner);
//随机生成正整数和分数的题目数量
Random random = new Random();
int simpleFourOperationNum = random.nextInt(titleNum) + 1;
int FractionOperationNum = titleNum - simpleFourOperationNum;
//分别获取用户做对相应题目的正确数
int simpleFourOpTrueNum = simpleFourOperation.generateSimpleFourOpExp(simpleFourOperationNum, scanner);
int fractionOpTrueNum = fractionOperation.generateFractionOpExp(FractionOperationNum, scanner);
//计算总分数
int trueNum = simpleFourOpTrueNum + fractionOpTrueNum;
int falseNum = titleNum - trueNum;
int score = 100 * trueNum / titleNum;
System.out.println("恭喜你完成这次练习,题目总数:" + titleNum + ", 你做对了" + trueNum + "道题,"
+ "做错了" + falseNum + "道题, " + "总分数为" + score + ", 继续加油!");
}
public static int handleUserInput(Scanner scanner) {
int titleNum;
do {
titleNum = scanner.nextInt();
if (titleNum > 0)
break;
else {
System.out.println("输入有误,不能输入非法字符以及小于0,请重新输入!");
System.out.println("请输入生成四则运算题目的个数:");
}
} while (!(titleNum > 0));
return titleNum;
}
}
运行结果:
第四部分:问题及解决方法
1.怎么解决先乘除后加减的问题?
先乘除后加减,在计算结果函数中遍历运算符数组中乘除符号后,在从存储运算数数组获取相对应的运算数进行优先计算,之后再对乘除计算之后的运算数进行相加减运算。
2.结果可以为负数,试了很多方法,最终找了一个好的方法?
定义一个在结果为负数和下一次循环的变量,用来控制题目数量,大于0就输出题目。
3.分数怎么约分?
将分子和分母的最小值的数值,从大到小去除分子和分母,如果能够同时被整除,则以此数值约分。
总结:
通过这次练习,明白了一个程序开发是一件不容易的事情,没有好的知识功底是无法创造出高质量高拓展的软件。天生我才必有用,千金散尽还复来,我会用这句话激励自己用功学习。
第五部分:个人软件过程耗时估计与统计表
PSP2.1 | Personal Software Process Stages | 预计hour | 实际hour |
Planning | 计划 | 0.1 | 0.3 |
· Estimate | 估计这个任务需要多少时间 | 5 | 9.0 |
Development | 开发 | 0.3 | 6.0 |
· Analysis | 需求分析 (包括学习新技术) | 0.4 | 0.4 |
· Design Spec | 生成设计文档 | 0.3 | 0.3 |
· Design Review | 设计复审 | 0.2 | 0.3 |
· Coding Standard | 代码规范 | 0.1 | 0.5 |
· Design | 具体设计 | 0.3 | 0.3 |
· Coding | 具体编码 | 2 | 2 |
· Code Review | 代码复审 | 0.3 | 0.5 |
· Test | 测试(自我测试,修改代码,提交修改) | 0.3 | 1.0 |