一、github地址
https://github.com/kinglc/AutoCalculator
二、PSP
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 |
30 |
·Estimate |
·估计这个任务需要多少时间 |
30 |
30 |
Development |
开发 |
2010 |
2160 |
·Analysis |
·需求分析(包括学习新技术) |
480 |
400 |
·Design Spec |
·生成设计文档 |
60 |
50 |
·Design Review |
·设计复审 |
30 |
30 |
·Coding Standard |
·代码规范 |
30 |
60 |
·Design |
·具体设计 |
240 |
300 |
·Coding |
·具体编码 |
900 |
1080 |
·Code Review |
·代码复审 |
90 |
60 |
·Test |
·测试 |
180 |
180 |
Reporting |
报告 |
70 |
70 |
·Test Report |
·测试报告 |
30 |
30 |
·Size Measurement |
·计算工作量 |
10 |
10 |
·Postmortem&Process Improvement Plan |
·事后总结并提出过程改进计划 |
30 |
30 |
|
合计 |
2110 |
2260 |
三、设计思路
1.整个项目包括生成题目及存储、解题、GUI、倒计时及历史记录功能。(队友博客地址https://home.cnblogs.com/u/shera)
2.解题:
按优先级分为小括号、两种乘方、乘除和加减。用while处理完算式中所有包含该运算符的部分,依次往后确定优先级。使用substring获取计算的数字,转为BigDecimal类型提高精度。
乘方运算中由于pow函数参数为double,为了保证精度此处使用for循环进行计算。
1 public String getAns(String s) { 2 try { 3 while (s.indexOf("(") != -1 && s.indexOf(")") != -1) {//小括号 4 int left; 5 int right; 6 String front; 7 String behind; 8 left = s.lastIndexOf("("); 9 front = s.substring(0, left); 10 s = s.substring(left + 1); 11 right = s.indexOf(")"); 12 behind = s.substring(right + 1); 13 s = s.substring(0, right); 14 s = front + getAns(s) + behind; 15 } 16 17 while (s.indexOf("^") != -1 || s.indexOf("**") != -1) {//乘方 18 String front; 19 int mul = (s.indexOf("^") + s.indexOf("**")); 20 int num = mul++; 21 while (num > 0) { 22 if (s.charAt(num) == '+' || s.charAt(num) == '-' || s.charAt(num) == '*' || s.charAt(num) == '÷') 23 break; 24 num--; 25 } 26 if(num!=0) 27 num++; 28 String a = s.substring(num, mul);//底数 29 front=s.substring(0,num); 30 num = mul + power_form; 31 while (num < s.length()) { 32 if (s.charAt(num) == '+' || s.charAt(num) == '-' || s.charAt(num) == '*' || s.charAt(num) == '÷') 33 break; 34 num++; 35 } 36 String b = s.substring(mul + power_form, num);//幂 37 int b1 = Integer.parseInt(b); 38 39 String c="1"; 40 if(a.indexOf('/')!=-1) 41 { 42 Fraction f1 = new Fraction(a); 43 for (int i = 0; i < b1; i++) 44 { 45 Fraction ft = new Fraction(c); 46 c = ft.multiply(f1); 47 } 48 } 49 else { 50 BigDecimal a1 = new BigDecimal(a); 51 BigDecimal ans = new BigDecimal(1); 52 for (int i = 0; i < b1; i++) 53 ans = ans.multiply(a1); 54 c = "" + ans; 55 } 56 s = front + c + s.substring(mul + 1 + power_form); 57 } 58 59 60 while (s.indexOf("*") != -1 || s.indexOf("÷") != -1) { 61 int multiply = s.indexOf("*"); 62 int divide = s.indexOf("÷"); 63 int way; 64 if (multiply == -1) 65 multiply = 0xfffffff; 66 if (divide == -1) 67 divide = 0xfffffff; 68 if (multiply < divide)//从左向右计算 69 way = multiply; 70 else way = divide; 71 int front = way - 1; 72 while (front > 0) { 73 if (s.charAt(front) == '+' || s.charAt(front) == '-') { 74 front++; 75 break; 76 } 77 front--; 78 } 79 String a = s.substring(front, way); 80 int behind = way + 1; 81 while (behind<s.length()) { 82 if (s.charAt(behind) == '+' || s.charAt(behind) == '-' || s.charAt(behind) == '*' || s.charAt(behind) == '÷') 83 break; 84 behind++; 85 } 86 String b = s.substring(way + 1, behind); 87 String c; 88 89 if(a.indexOf('/')==-1&&b.indexOf('/')==-1) { 90 BigDecimal a1 = new BigDecimal(a); 91 BigDecimal b1 = new BigDecimal(b); 92 if (divide == way) 93 c = a1.divide(b1,3, BigDecimal.ROUND_HALF_UP).toString(); 94 else c = a1.multiply(b1).toString(); 95 } 96 else{ 97 Fraction f1 = new Fraction(a); 98 Fraction f2 = new Fraction(b); 99 if (divide == way) 100 c = f1.divide(f2); 101 else c = f1.multiply(f2); 102 } 103 s = s.substring(0, front) + c + s.substring(behind); 104 } 105 106 107 while (s.indexOf("+") != -1 || s.indexOf("-") != -1) { 108 int plus = s.indexOf("+"); 109 int minus = s.indexOf("-"); 110 int way; 111 if (plus == -1) 112 plus = 0xfffffff; 113 if (minus == -1) 114 minus = 0xfffffff; 115 if (plus < minus) 116 way = plus; 117 else way = minus; 118 int front = way - 1; 119 while (front > 0) { 120 if (s.charAt(front) == '+' || s.charAt(front) == '-') { 121 front++; 122 break; 123 } 124 front--; 125 } 126 String a = s.substring(front, way); 127 int behind = way + 1; 128 while (s.substring(behind).length() > 0) { 129 if (s.charAt(behind) == '+' || s.charAt(behind) == '-') 130 break; 131 behind++; 132 } 133 String b = s.substring(way + 1, behind); 134 String c; 135 136 if(a.indexOf('/')==-1&&b.indexOf('/')==-1) { 137 BigDecimal a1 = new BigDecimal(a); 138 BigDecimal b1 = new BigDecimal(b); 139 if (minus == way) 140 c = a1.subtract(b1).toString(); 141 else c = a1.add(b1).toString(); 142 } 143 else{ 144 Fraction f1 = new Fraction(a); 145 Fraction f2 = new Fraction(b); 146 if (minus == way) 147 c = f1.substract(f2); 148 else c = f1.add(f2); 149 } 150 s = s.substring(0, front) + c + s.substring(behind); 151 } 152 153 if(s.indexOf('.')!=-1) 154 s=s.substring(0,s.length()-1); 155 return s; 156 157 } catch (Exception e) { 158 return "计算部分出错"; 159 } 160 }
分数单独开一个类,分为分子,分母进行计算。类中内含加减乘除约分方法,其中约分为private。
1 public class Fraction { 2 int nume;//分数的分子 3 int deno;//分数的分母 4 5 public Fraction() {} 6 7 public Fraction(String s) 8 { 9 int frac=s.indexOf('/'); 10 if(frac==-1)//若是整数,分母按1算 11 { 12 this.nume=Integer.parseInt(s); 13 this.deno=1; 14 } 15 else{ 16 this.nume=Integer.parseInt(s.substring(0,frac)); 17 this.deno=Integer.parseInt(s.substring(frac+1)); 18 } 19 } 20 21 public String add(Fraction f) 22 { 23 Fraction ans=new Fraction(); 24 ans.deno=this.deno*f.deno/gcd(this.deno,f.deno);//通分 25 this.nume*=ans.deno/this.deno; 26 f.nume*=ans.deno/f.deno; 27 ans.nume=this.nume+f.nume; 28 return reduction(ans); 29 } 30 31 public String substract(Fraction f) 32 { 33 Fraction ans=new Fraction(); 34 ans.deno=this.deno*f.deno/gcd(this.deno,f.deno);//通分 35 this.nume*=ans.deno/this.deno; 36 f.nume*=ans.deno/f.deno; 37 ans.nume=this.nume-f.nume; 38 return reduction(ans); 39 } 40 41 public String divide(Fraction f) 42 { 43 Fraction ans=new Fraction(); 44 ans.deno=this.deno*f.nume; 45 ans.nume=this.nume*f.deno; 46 return reduction(ans); 47 } 48 49 public String multiply(Fraction f) 50 { 51 Fraction ans=new Fraction(); 52 ans.deno=this.deno*f.deno; 53 ans.nume=this.nume*f.nume; 54 return reduction(ans); 55 } 56 57 static String reduction(Fraction f)//约分 58 { 59 int div=gcd(f.deno,f.nume); 60 f.deno/=div; 61 f.nume/=div; 62 if(f.deno==1) 63 return ""+f.nume; 64 else if(f.nume==0) 65 return "0"; 66 else return f.nume+"/"+f.deno; 67 } 68 69 static int gcd( int x , int y){//最大公约数 70 if( y == 0 ) 71 return x; 72 else 73 return gcd(y,x%y); 74 } 75 }
3.GUI
使用自由度相对较高的FlowLayout进行布局,添加JLabel,JButton,JTextField控件。对于乘方的设置:
1 jrb1.setFont(new java.awt.Font("Dialog", 1, 15)); 2 jrb2.setFont(new java.awt.Font("Dialog", 1, 15)); 3 ButtonGroup bg = new ButtonGroup(); 4 bg.add(jrb1); 5 bg.add(jrb2); 6 jrb1.setSelected(true);
4.倒计时
使用Thread.sleap(1000)计时,若不提交答案时间结束则重置时间,减少生命,并重置算式。
1 while (time >= 0) { 2 jltime.setText( "" + time ); 3 try { 4 Thread.sleep(1000); 5 } catch (InterruptedException e) { 6 e.printStackTrace(); 7 } 8 time--; 9 if(time==-1) { 10 time=20; 11 life--; 12 jllife.setText("生命"+life); 13 jltime.setText("时间"+time); 14 Create c= new Create(power_form); 15 jlformula.setText(c.formula); 16 17 } 18 }
5.历史记录
新建一个score.txt文件,每次得分为0时FileWriter.writer会在文件末尾写入时间和得分,并弹窗显示最新的十条记录。
1 try { 2 Calendar c = Calendar.getInstance(); 3 FileWriter writer = new FileWriter(scorefile, true); 4 writer.write(c.get(Calendar.MONTH)+1+"月"+c.get(Calendar.DAY_OF_MONTH)+"日" 5 +c.get(Calendar.HOUR_OF_DAY)+"时"+c.get(Calendar.MINUTE)+"分" + 6 " 得分: "+score+"\n"); 7 writer.close(); 8 } catch (IOException e1) { 9 e1.printStackTrace(); 10 } 11 try { 12 time=-2; 13 showscores(); 14 end(); 15 } catch (IOException e1) { 16 e1.printStackTrace(); 17 }
1 public void showscores() throws IOException { 2 BufferedReader bfr = new BufferedReader(new FileReader(scorefile)); 3 String score[]= new String[1000]; 4 String s,ans=""; 5 int pos=0; 6 while ((s=bfr.readLine())!=null){ 7 score[pos++]=s; 8 } 9 for(i=pos-10;i<pos;i++) 10 if(i>=0){ 11 ans+=score[i]+"\n"; 12 } 13 JOptionPane.showMessageDialog(null, ans, "历史记录", JOptionPane.INFORMATION_MESSAGE); 14 15 } 16 17 public void end(){ 18 System.exit(0); 19 }
三、总结
首先从知识方面,熟悉了java的GUI,对文件的操作,加深了类的认识,更了解了一些此前完全没有接触过的类和方法。
从项目经验来说,结对项目就存在着交互。项目一共四个类,Fraction和Calculator是我写的,Create是队友写的,Fraction只在Calculator中调用,都没有什么大问题。而MainJF是不断在两个人之间交替,我写完GUI计时计算,队友接着写生成,生成之后两人一起研究历史记录,历史记录有涉及到之前的计时,就会难免杂乱。不过也是由于其他事情比较多没有在一段时间内专心做这一项,没能顺畅地走下一套流程。不过这也是很难得的一次经历了。