coding.net源码仓库地址:https://git.coding.net/day_light/szysmaster1.git
一、需求分析
(一)题目:尝试按照《构建之法》第2章中2.3所述PSP流程,使用JAVA编程语言,独立完成一个3到5个运算符的四则运算练习的命令行软件 开发。软件基本功能要求如下:
- 程序可接收一个输入参数n,然后随机产生n道加减乘除(分别使用符号+-*÷来表示)练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。
- 为了让小学生得到充分锻炼,每个练习题至少要包含2种运算符。同时,由于小学生没有分数与负数的概念,你所出的练习题在运算过程中不得出现负数与非整数,比如不能出 3÷5+2=2.6,2-5+10=7等算式。
- 练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致。
- 当程序接收的参数为4时,以下为一个输出文件示例。
2018010203
13+17-1=29 11*15-5=160 3+10+4-16=1 15÷5+3-2=4
软件附加功能要求如下:支持有括号的运算式,包括出题与求解正确答案。注意,算式中存在的括号必须大于2个,且不得超过运算符的个数。(5分)扩展程序功能支持真分数的出题与运算(只需要涵盖加减法即可),例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6,且计算过程中与结果都须为真分数。(5分)
(二)分析
1.使用random()产生3~5个随机数来做运算符,即产生4~6个随机数来做运算数。
2.由于小学生运算不能产生负数、整数且除数不能为0,解决方案:当出现上述情况时,重新产生随机数。
3.运算结果的计算实现:边产生随机数边计算,不断更新sum。
4.支持括号运算。
5.部分真分数
6.利用IO输出流将学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中。
二、功能设计
(一)基本功能:随机产生n道加减乘除(分别使用符号+-*÷来表示)练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间
(二)扩展功能:支持括号运算、真分数运算
三、设计实现:设计包括你会有哪些类,这些类分别负责什么功能,他们之间的关系怎样?你会设计哪些重要的函数,关键的函数是否需要画出流程图?函数之间的逻辑关系如何?
(一)共包含两个类文件,Main为主函数,负责输出到result.txt文件;Lib是辅助类,compute是支持带括号的整数加减乘除运算,fraction是负责真分数加减运算;max求最大公约数。
(二)调用关系如图所示
四、算法详解
(一)主函数
1 public class Main {
2 public static void main(String[] args) {
3 int n = 0 ;
4 try
5 {
6 n=Integer.parseInt(args[0]);
7 }
8 catch (Exception e)
9 {
10 System.out.println("非法字符,请输入1~1000内的整数:");
11 }
12 try {
13 PrintStream ps = new PrintStream("../result.txt");
14 System.setOut(ps);
15 }catch(Exception e) {
16 System.out.println("文件创建失败!");
17 }
18 System.out.println("2016012020");
19 Lib.compute(n);//整数运算
20 Lib.fraction(n);//真分数运算
21 }
22 }
(二)整数计算使用边生成,边计算的方式,每一次运算均更新sum并输出arr1 = "" + oper[s1] + "" + b1;以下为核心代码,完整代码见coding.net
1 for (int i = 1; i < th; i++) {
2
3 int s1 = random.nextInt(5);
4 int b1 = random.nextInt(100);
5 a = sum; //更新sum
6 if (oper[s1] == ' ') {
7 s1++;
8 }
9 if (oper[s1] == '-') {
10 while (a - b1 < 0) {
11 b1 = random.nextInt(100);
12 }
13 sum = a - b1;
14 }
15 if (oper[s1] == '/') {
16 if (b1 == 0)
17 b1++;
18 while (a % b1 != 0) {
19 b1 = random.nextInt(99) + 1;
20 }
21 sum = a / b1;
22 }
23 if (oper[s1] == '+')
24 sum = a + b1;
25 if (oper[s1] == '*')
26 sum = a * b1;
27 String arr1 = "";
28 arr1 = "" + oper[s1] + "" + b1;
(三)使用fraction类方法实现真分数出题实现:生成四个随机数,分别表示分子、分母;当分子大于分母时(即假分数出现时),交换分子分母,当分子分母有公因数时,调用max方法实现化简,当中间的计算结果出现假分数时,重新出题。
1 //真分数运算
2 static public void fraction(int n) { // a为分子1,b为分子2
3 for (int j = 0; j < n; j++) {
4 Random rand = new Random();
5 int a = rand.nextInt(10) + 1;
6 int Fm1 = rand.nextInt(10) + 1;// 分母1
7 if (a > Fm1) {
8 int t = a;
9 a = Fm1;
10 Fm1 = t;
11 }
12 char[] oper = { '+', '-' };
13 int s = rand.nextInt(2);
14 int b = rand.nextInt(10) + 1;
15 int Fm2 = rand.nextInt(10) + 1;// 分母2
16 if (b > Fm2) {
17 int t = b;
18 b = Fm2;
19 Fm2 = t;
20 } // 化简
21 int ha = a / max(a, Fm1);
22 int hFm1 = Fm1 / max(a, Fm1);
23 int hb = b / max(b, Fm2);
24 int hFm2 = Fm2 / max(b, Fm2);
25 if (oper[s] == '+') {
26 a = a * Fm2 + b * Fm1;
27 int F = Fm1 * Fm2;
28 if (a > F) {
29 a = rand.nextInt(6) + 1;
30 Fm1 = rand.nextInt(6) + 1;// 分母1
31 }
32 a = a / max(a, F);
33 Fm1 = F / max(a, F);
34 }
35 if (oper[s] == '-') {
36 a = a * Fm2 - b * Fm1;
37 int F = Fm1 * Fm2;
38 if (a > F || a < 0) {
39 a = rand.nextInt(10) + 1;
40 Fm1 = rand.nextInt(10) + 1;// 分母1
41 }
42 a = a / max(a, F);
43 Fm1 = F / max(a, F);
44 }
45 System.out.print(ha + "/" + hFm1 + oper[s] + b + "/" + Fm2);
46 int th = (int) (Math.random() * 2) + 3;// 3~5个运算符
47 for (int i = 1; i < th; i++) {
48 Random random = new Random();
49 int s1 = random.nextInt(2);
50 int b1 = random.nextInt(10) + 1;
51 int Fmt = random.nextInt(10) + 1;// 分母2
52 if (a > Fm1) {
53 int t = a;
54 a = Fm1;
55 Fm1 = t;
56 }
57 if (b1 > Fmt) {
58 int t = b1;
59 b1 = Fmt;
60 Fmt = t;
61 } // 化简
62 int a1 = a / max(a, Fm1);
63 int Fm11 = Fm1 / max(a, Fm1);
64 int hb1 = b1 / max(b1, Fmt);
65 int hFmt = Fmt/ max(b1, Fmt);
66 if (oper[s1] == '+') {
67
68 a = a * Fmt + b * Fm1;
69 int F = Fm1 * Fmt;
70
71 if (a > F) {
72 a = rand.nextInt(10) + 1;
73 Fm1 = rand.nextInt(10) + 1;// 分母1
74 }
75 a = a / max(a, F);
76 Fm1 = F / max(a, F);
77 }
78 if (oper[s1] == '-') {
79
80 a = a * Fmt - b * Fm1;
81 int F = Fm1 * Fmt;
82 if (a > F || a < 0) {
83 a = rand.nextInt(10) + 1;
84 Fm1 = rand.nextInt(10) + 1;// 分母1
85 }
86 a = a / max(a, F);
87 Fm1 = F / max(a, F);
88 }
89 String arr1 = "";
90 if (a < Fm1 && a >= 0) {
91 arr1 = oper[s1] + "" + hb1 + "/" + hFmt;
92 System.out.print(arr1);
93 }
94 }
95 System.out.println("=");
96 // System.out.println("=" + a + "/" + Fm1);
97 }
98 }
五、测试运行
(一)命令行测试:
(二)输出到result.txt文件
(三)生成文件位置
六、比较独特的的代码片段
括号优先级的判定
1 char[] oper = { '+', '-', ' ', '*', '/' };
2 if (i == 1) {
3 k = s;
4 if (s1 - k >= 2) {
5 System.out.print(")" + arr1);
6 } else {
7 System.out.print(arr1);
8 }
9 }
10
11 if (i != 1) {
12 if (s1 - k >= 2) {
13 System.out.print(")" + arr1);
14 } else {
15 System.out.print(arr1);
16 }
17 k = s1;
18 }
七、总结:你设计的程序如何实现软件设计的'模块化'原则。
1.整个项目分为两个源文件,Main和Lib辅助类,实现了整体的模块化。
2.在Lib辅助类中调用了fractoin和caculate方法,在fraction方法中使用了max函数,求最大公约数,实现了辅助类里的模块化。
采用模块化编程,方便调试,当运行报错时,可以分别测试每一个模块;还有利于移植,当编程时需要实现相同或相似的功能,可以只做小部分改动,减小了代码的冗余度。
八、展示PSP
任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) | |
Planning | 计划 | 8 | 6 |
· Estimate | · 估计这个任务需要多少时间,并规划大致工作步骤 | 3天 | 4天 |
Development | 开发 | 82 | 88 |
· Analysis | · 需求分析 (包括学习新技术) | 30 | 45 |
· Design Spec | · 生成设计文档 | 5 | 8 |
· Design Review | · 设计复审 (和同事审核设计文档) | 4 | 6 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 15 |
· Design | · 具体设计 | 10 | 30 |
· Coding | · 具体编码 | 180 | 240 |
· Code Review | · 代码复审 | 8 | 20 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 120 |
Reporting | 报告 | 9 | 6 |
· Test Report | · 测试报告 | 3 | 2 |
· Size Measurement | · 计算工作量 | 2 | 1 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 10 |
九、总结
因为之前没有进行过单元测试,所以总结了自己在进行单元测试时遇到的问题。
(一)找不到或无法加载主类
查阅网上资料出现此种情况,有两类原因:
1.环境配置
(1)检验方法:输出java、javac、java -version
2.在你在eclipse写的时候里面会有package,把它删掉就可以了(我属于这种情况)
PS:同时要注意编译的java源文件有没有包含Main函数
(二)当源文件不止一个时,所有源文件要一起编译,即javac *.java
十、反思与不足
在编程时有时会为了加快编程进度,使用的方法不够简洁高级,忽略了代码的质量,导致后期功能扩展时很困难,有时觉得自己省了时间,实际上到了要实现后期功能时举步维艰,反而延长了整体编程的时间。
在扩展功能上,实现了”)“而非”()“;未能成功输出真分数的计算结果。
十一、参考博客
【1】初学者利用git 上传代码到Coding的简单操作步骤
【2】错误提示:fatal: remote origin already exists.