项目地址
https://github.com/Destinywck/Calculator
第一部分:初步设想
根据题目要求功能,需要实现一个分数四则运算计算器,一个题目随机生成器。在计算器中加入对题目合法性和结果正确性的判断,正确情况会输出正确答案,其他情况结果均为"null";在题目生成器中加入对生成题目的限制条件,利用计算器判断生成式的合法性,并对题目进行判重去重操作。
后续发现要求需要实现对输入题目的判题并给出试卷得分,加入判题部分,捕捉屏幕输入表达式答案,使用计算器判断正误,并以此统计得分情况。
第二部分:具体实现
1、计算器部分
计算器实现为Calculator.java,比较冗杂,暂时没有时间整理它,对输入字符串格式表达式进行计算,支持括号,分数计算,支持表达式合法性判断。
分数计算过程实现为CalculatorForFraction.java,将所有形似操作数转换为分数进行计算之后返回结果。比较简单,具体不做阐述。
计算过程:Calclulator.exec().
1 public void exec() { 2 try { 3 clean(); 4 if (expression == null || expression.isEmpty()) { 5 throw new IllegalArgumentException("Blank Expression!"); 6 } 7 opStack.push(TERMINATE_TOKENS.START_END_MARK); 8 // break the String format expression into tokens. 9 this.tokens = TOKENIZER.exec(expression 10 + TERMINATE_TOKENS.START_END_MARK); 11 for (; i < tokens.size(); i++) { 12 final Object token = tokens.get(i); 13 if (token instanceof Double) { 14 // store the fraction operand 15 processOperand((Double) token); 16 } else if(token instanceof Integer) { 17 // store the integer operand 18 processOperand((Integer) token); 19 } else { 20 // store the operator 21 processOperator((Character) token); 22 } 23 } 24 } catch (Throwable e) { 25 // illegal expression 26 isLegal = false; 27 illegalMessage = String.format( 28 "Incorret Expression: %s\nError: %s", expression, 29 e.getMessage()); 30 } 31 }
获取结果:Calclulator.getResult().
1 /** 2 * get the result, if the result dose not satisfy the requirement, the return 3 * will be null. 4 * @return 5 */ 6 public String getResult(){ 7 String arr[] = (result + "").split("\\."); 8 // limit the size of result. 9 if(result > 0 && arr[0].length() <= 7 && arr[1].length() <= 7 && Integer.valueOf(arr[1]) != 0){ 10 if(Integer.valueOf(arr[0]) % Integer.valueOf(arr[1]) == 0) 11 return (Integer.valueOf(arr[0]) / Integer.valueOf(arr[1])) + ""; 12 return (result + "").replaceAll("\\.", "/"); 13 } 14 return null; 15 }
获取合法性:Calculator.isLegal().
比如判断分数出现时分母是否为0,但出现分母为0的情况时,抛出异常,Calclulator.exec()执行时会将表达式判为非法。其他非法表达式判断情况类似。
1 if(isFraction){ 2 if(Double.valueOf(PREVIOUS_CHAR.toString()) != 0){ 3 isFraction = false; 4 result.add(Double.valueOf(BUFFER.toString())); 5 BUFFER.delete(0, BUFFER.length()); 6 } else 7 throw new IllegalArgumentException("Denominator is incorrect! (can not be zero)"); 8 }
2、题目生成器
题目生成器实现为GenerateProblem.java,可根据输入参数,题目个数,操作符个数,包含分数个数,是否加入括号等,生成满足要求的合法表达式及其结果。
生成题目:genProblem(int numOfProblem, int numOfOperator, int numOfFraction, boolean hasBrace),生成给定条件的表达式及其结果。
存在的问题,题目判重部分,只是判断表达式的字符串形式是否完全相同,不同则为不同题目,出现的问题就是 (1+3)+3-1和1+3+3-1并不能判断为重复题目(如果小学题目算作重复的话),有待改进。
/**
* get problems with specific requirement.
* @param numOfProblem
* how many problems we want.
* @param numOfOperator
* how many operators we want, and the operand is one more than this number.
* @param numOfFraction
* how many fractions we want to see in each problem.
* @param hasBrace
* if we want the problem contains brace, this option should be "true".
* @return
* a map of problem contains the expression and its answer.
*/
public Map<String, String> genProblem(int numOfProblem, int numOfOperator, int numOfFraction, boolean hasBrace){
// get the expression.
// judge the legality.
// remove the repeated expression.
}
生成操作数:genOperand().随机生成指定个数的1-100之间的整数操作数。操作符生成过程类似。
1 /** 2 * get specific number of operand randomly, the operand is between 1 and 100. 3 * @param number 4 * how many operands we want. 5 * @return 6 * a stack of operand 7 */ 8 private Stack<Integer> genOperand(int number){ 9 Stack<Integer> result = new Stack<Integer>(); 10 while(result.size() < number) 11 result.push(1 + random.nextInt(100)); 12 return result; 13 }
生成表达式:genExpression(int numOfOperator),genExpression(int numOfOperator, int numOfFraction)。两个方法分别可以生成包含和不包含分数形式的指定操作符的表达式。
加入括号:addBrace(List<String> exp). 对给定表达式加入随机个数的括号,随机个税根据表达式中运算符"+"和"-"的总数产生,做了一个简单粗暴的处理,括号必须包含"+"或者"-",因此括号的对数不可以超过表达式中运算符"+"和"-"的总数。
1 /** 2 * generate expression with specific number of brace 3 * @param expression 4 * the expression we want to process. 5 * @param span 6 * abandoned. not realized so far. 7 * @return 8 * the expression with brace. 9 */ 10 public String addBrace(List<String> exp){ 11 List<String> resultList = new ArrayList<String>(); 12 int opsNum = 0; 13 for (int i = 0; i < exp.size(); i++) { 14 if(exp.get(i).equals("+") || exp.get(i).equals("-")) 15 opsNum++; 16 } 17 // get the number of braces according to the expression randomly. 18 int braceNumber = 0; 19 if(opsNum > 0) 20 braceNumber = random.nextInt(opsNum); 21 22 String lastOps = ""; 23 boolean readForRightBrace = false; 24 for(int i = 0; i < exp.size(); i++){ 25 resultList.add(exp.get(i)); 26 if(operator.contains(exp.get(i).charAt(0))){ 27 lastOps = exp.get(i); 28 if(braceNumber > 0){ if((lastOps.equals("+") || lastOps.equals("-")) && !readForRightBrace){ 29 String temp = resultList.get(resultList.size() - 2); 30 if(!temp.equals(")")){ 31 resultList.remove(resultList.size() - 1); 32 resultList.remove(resultList.size() - 1); 33 resultList.add("("); 34 resultList.add(temp); resultList.add(lastOps); 35 readForRightBrace = true; 36 } 37 } 38 } 39 continue; 40 } 41 if(readForRightBrace){ resultList.add(")"); 42 readForRightBrace = false; 43 braceNumber--; 44 } 45 } 46 String result = ""; 47 for(String str : resultList) 48 result += str; 49 return result.replaceAll("\\.", "/"); 50 }
3、命令行交互
程序入口为App.java,支持命令行简单交互操作。
'c' for calcultor, 'g' for problem generator, 'j' for judgement, 'q' for quit.
(1) 计算器模式:
选择calculator模式,输入表达式,给出结果及其合法性。
(2) 题目生成模式:
选择problem generator模式,输入生成题目的要求,给出题目及其结果。
(3) 判卷模式:
选择judgement模式,输入生成题目要求,对每个生成的题目输入答案之后给出判断并做最后判卷。
第三部分:简单测试
时间有限,只使用手动输入异常情况的方法检测实现正确性。
1、计算器正确性测试
输入给定表达式,使用计算器计算并校对结果及其合法性;
输入给定条件,然后判断生成表达式是否合法。
2、速度测试
机器参数
生成10000条,5个操作数,期中包含2个分数,包含括号的表达式。
生成100000条,5个操作数,期中包含2个分数,包含括号的表达式。
2、存在问题
后续待改善问题
1. 题目判重实现不完善;
2. 括号冗余偶尔出现,未加处理。
第四部分:PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 5 |
· Estimate | · 估计这个任务需要多少时间 | 10 | 5 |
Development | 开发 | 600 | 610 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 50 |
· Design Spec | · 生成设计文档 | 10 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
· Design | · 具体设计 | 80 | 100 |
· Coding | · 具体编码 | 200 | 280 |
· Code Review | · 代码复审 | 30 | 20 |
· Test | · 测试(自我测试,修改代码,提交修改) | 170 | 100 |
Reporting | 报告 | 200 | 200 |
· Test Report | · 测试报告 | 80 | 80 |
· Size Measurement | · 计算工作量 | 20 | 20 |
·Postmortem& Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 100 | 100 |
合计 | 810 | 815 |
总结
认识到了设计一个项目的重要性,而不是上来就敲代码,但是自己高度感觉很有限,最后会被自己绕晕。当然这个项目不是很大,我尽量考虑为以后的扩展留出空间,但是确实比较吃力,因为看不到一个完整的大项目应该是怎么样的,之后考虑去看看一些开源项目的源码结构设计,希望自己可以更加从容些。
这次并没有使用java的一些测试工具对程序进行分析,时间确实有限,投入精力有限,收获也就有限,但是意识到了一些以后需要注意的问题,算是最大的收获了。