姓名 | 学号 | Github项目地址 |
---|---|---|
薛秋昊 | 3122004369 | https://github.com/0818XR/0818XR/tree/main/3122004369/GenerateArithmeticProblems |
曾俊涛 | 3122004373 |
一.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟 | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
Estimate | 估计这个任务需要多少时间 | 200 | 200 |
Development | 开发 | 100 | 150 |
Analysis | 需求分析 (包括学习新技术) | 20 | 15 |
Design Spec | 生成设计文档 | 10 | 10 |
Design Review | 设计复审 (和同事审核设计文档) | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 25 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 120 | 150 |
Code Review | 代码复审 | 30 | 25 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 20 |
Reporting | 报告 | 60 | 60 |
Test Report | 测试报告 | 20 | 20 |
Size Measurement | 计算工作量 | 5 | 5 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 20 |
合计 | 695 | 750 |
二.性能分析
采用JProfiler分析,结果如下图所示:
三.设计实现过程
3.1类
程序定义了名为ExpressionEvaluator的类
3.2函数
主要包括五个函数,如下所示:
public static double evaluate(String expression):用于接收一个字符串形式的算术表达式,并返回其计算结果。
private static double parseValue(String token):用于将字符串形式的数值(可能包含整数、分数或整数与分数的组合)解析为double类型的值。
private static boolean isNumeric(String token):用于判断给定的字符串是否可以解析为一个数值(整数或分数)。
private static boolean hasPrecedence(char op1, char op2):用于判断两个运算符之间的优先级关系。
private static double applyOperation(char operator, double b, double a):用于根据给定的运算符和两个操作数执行算术运算。
3.3关系
parseValue 方法:被evaluate方法调用,用于将标记(token)解析为double类型的数值。
isNumeric 方法:被evaluate方法用于判断一个标记是否可以解析为数值。使用正则表达式来匹配整数和分数的格式。
hasPrecedence 方法:被evaluate方法用于判断两个运算符之间的优先级。乘法和除法具有高于加法和减法的优先级,括号可以改变运算的优先级。
applyOperation 方法:被evaluate方法调用,用于执行实际的算术运算。根据给定的运算符和两个操作数返回运算结果。
四.代码说明
-
evaluate 方法:主方法,负责整个表达式的求值过程
` public static double evaluate(String expression) {// 预处理表达式,去除单引号并分割成tokens expression = expression.replace("'", " "); String[] tokens = expression.split(" "); Stack<Double> values = new Stack<>(); // 用于存储数值 Stack<Character> operators = new Stack<>(); // 用于存储运算符 // 遍历每个token for (String token : tokens) { if (token.isEmpty()) continue; // 跳过空token // 如果token是数值 if (isNumeric(token)) { values.push(parseValue(token)); } // 如果token是左括号 else if (token.equals("(")) { operators.push('('); } // 如果token是右括号 else if (token.equals(")")) { // 弹出并计算括号内的表达式,直到遇到左括号 while (!operators.isEmpty() && operators.peek() != '(') { values.push(applyOperation(operators.pop(), values.pop(), values.pop())); } operators.pop(); // 弹出左括号 } // 如果token是运算符 else { // 应用运算符优先级规则 while (!operators.isEmpty() && hasPrecedence(token.charAt(0), operators.peek())) { if (values.size() < 2) break; // 确保有足够的操作数 values.push(applyOperation(operators.pop(), values.pop(), values.pop())); } operators.push(token.charAt(0)); // 将当前运算符入栈 } } // 处理剩余的运算符 while (!operators.isEmpty()) { if (values.size() < 2) break; // 确保有足够的操作数 values.push(applyOperation(operators.pop(), values.pop(), values.pop())); } // 返回最终结果或0(如果栈为空) return values.isEmpty() ? 0 : values.pop();}`
2.parseValue 方法:负责将字符串形式的数值(整数、分数或整数与分数的组合)解析为double类型。
` private static double parseValue(String token) {
// 如果token包含分数
if (token.contains("/")) {
String[] parts = token.split("/");
return (double) Integer.parseInt(parts[0]) / Integer.parseInt(parts[1]);
}
// 如果token包含空格(假设用于分隔整数和分数)
else if (token.contains(" ")) {
String[] parts = token.split(" ");
int whole = Integer.parseInt(parts[0]);
String[] fractionParts = parts[1].split("/");
double fraction = (double) Integer.parseInt(fractionParts[0]) / Integer.parseInt(fractionParts[1]);
return whole + fraction;
}
// 否则,直接解析为double
else {
return Double.parseDouble(token);
}
}`
3.isNumeric 方法:用于判断给定的字符串是否可以解析为数值。
`private static boolean isNumeric(String token) {
// 使用正则表达式匹配整数和可能带有分数的整数
return token.matches("-?\\d+( \\d+\\d+)?"); }`
4.hasPrecedence 方法:用于判断两个运算符之间的优先级。
`private static boolean hasPrecedence(char op1, char op2) {
// 括号优先级最高,不参与比较
if (op2 == '(' || op2 == ')') return false;
// 乘法和除法优先级高于加法和减法
return !(op1 == '*' || op1 == '/') || (op2 != '*' && op2 != '/'); }`
5.applyOperation 方法:负责根据给定的运算符和两个操作数执行算术运算。
`private static double applyOperation(char operator, double b, double a) {
switch (operator) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return 0;}`
五.测试运行
十个测试用例如下:
答案如下:
六.项目总结
在本次结对项目中,我们共同开发了一个四则运算的计算器程序。该项目不仅考验了我们的编程技能,也加深了我们之间的沟通与协作能力。在整个开发过程中,我们保持了良好的沟通与协作,及时分享各自的想法与进展,有效解决了遇到的问题。通过相互审查代码,我们确保了代码的质量与可读性,同时也从对方身上学到了不少编程技巧。于此同时,还存着不足之处。虽然实现了基本功能,但在算法优化、内存使用等方面还有提升空间。这次结对项目是一次非常宝贵的经历。我们不仅在技术上取得了进步,更在团队协作与沟通能力上得到了锻炼。