结对项目
| 这个作业属于哪个课程 | 软工1班 |
| 这个作业要求在哪里| 作业三 |
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
| 项目成员 | 李秉泉312004147|王瑞3123004155 |
github仓库连接:我的仓库
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| · Estimate | · 估计这个任务需要多少时间 | 240 | 480 |
| Development | 开发 | ||
| · Analysis | · 需求分析 (包括学习新技术) | 120 | 100 |
| · Design Spec | · 生成设计文档 | 20 | 20 |
| · Design Review | · 设计复审 | 20 | 10 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
| · Design | · 具体设计 | 60 | 75 |
| · Coding | · 具体编码 | 60 | 360 |
| · Code Review | · 代码复审 | 40 | 20 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 40 | 30 |
| Reporting | 报告 | ||
| · Test Repor | · 测试报告 | 60 | 40 |
| · Size Measurement | · 计算工作量 | 20 | 20 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 40 |
| · 合计 | 740 | 1205 |
一、接口设计和实现过程
类结构
- Main : 主程序入口
- AnswerCalculator : 答案生成模块
- ArithmeticGenerator : 题目生成模块
- Expression.java : 表达式模块,包含数字表达式和运算符表达式及其父类
- AnswerGrader : 判定题目和答案的对错,并统计结果
- FileHandler : 文件读写模块
- CommandLineParser : 命令行参数处理
二、效能分析



性能提升:使用有限重试+安全降级机制,防止代码死循环,减少递归生成无效子树次数
三、核心代码
//递归生成四则运算表达式树
private static Expression generateExpression(int opsLeft, int num) {
//保证题目至少有一个运算符
if (opsLeft == 0 || random.nextDouble() < 0.3 - 0.1 * opsLeft) {
return new NumberExpression(generateNumber(num));
} else {
// 新增:最大重试次数
final int MAX_RETRIES = 15;
int retryCount = 0;
char op = "+-×÷".charAt(random.nextInt(4));
int remainingOps = opsLeft - 1;
// 动态分配剩余配额给左右子树
int leftOps = random.nextInt(remainingOps + 1);
int rightOps = remainingOps - leftOps;
Expression right;
Expression left;
Fraction rightVal, leftVal;
do { // 外层循环控制整个表达式生成
// 生成右子树(可能包含内层循环)
right = generateExpression(rightOps, num);
rightVal = right.calculate();
// 内层重试机制
boolean needRetry;
do {
needRetry = false;
left = generateExpression(leftOps, num);
leftVal = left.calculate();
// 条件1:减法校验
if (op == '-' && leftVal.subtract(rightVal).isNegative()) {
needRetry = true;
}
// 条件2:除法校验
if (op == '÷') {
// 除法右子树非零校验
while (isZero(rightVal)) {
if (++retryCount > MAX_RETRIES) break;
right = generateExpression(rightOps, num);
rightVal = right.calculate();
}
// 真分数校验
if (!isProperFraction(leftVal.divide(rightVal))) {
needRetry = true;
}
}
// 全局结果非负校验
if (!needRetry) {
Fraction result = switch (op) {
case '+' -> leftVal.add(rightVal);
case '-' -> leftVal.subtract(rightVal);
case '×' -> leftVal.multiply(rightVal);
case '÷' -> leftVal.divide(rightVal);
default -> throw new IllegalArgumentException("Invalid operator");
};
if (result.isNegative()) {
needRetry = true;
}
}
// 重试控制
if (needRetry && ++retryCount > MAX_RETRIES) {
// 降级策略:生成最简单的合法表达式
return new OperatorExpression('+',
new NumberExpression(generateNumber(num)),
new NumberExpression(generateNumber(num))
);
}
} while (needRetry);
break; // 退出外层循环
} while (true);
return new OperatorExpression(op, left, right);
}
}
//将分数转换为最简形式。
private void normalize() {
//处理分母为负数的情况。
if (denominator < 0) { numerator *= -1; denominator *= -1; }
int gcd = gcd(Math.abs(numerator), denominator);
numerator /= gcd; denominator /= gcd;
//将假分数转换为带分数(如 7/4 → `1'3/4
if (numerator >= denominator) {
whole += numerator / denominator; numerator %= denominator;
}
}
//计算两个整数的最大公约数,用于分数约分
private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
private void advance() {
// 跳过空格
if (pos >= input.length()) {
currentToken = new Token(TokenType.EOF, "");
return;
}
char ch = input.charAt(pos++);
while (Character.isWhitespace(ch)){
if (pos >= input.length()) {
currentToken = new Token(TokenType.EOF, "");
return;
}
ch = input.charAt(pos++);
}
switch (ch) {
case '+': currentToken = new Token(TokenType.PLUS, "+"); break;
case '-': currentToken = new Token(TokenType.MINUS, "-"); break;
case '×': currentToken = new Token(TokenType.MULTIPLY, "×"); break;
case '÷': currentToken = new Token(TokenType.DIVIDE, "÷"); break;
case '(': currentToken = new Token(TokenType.LPAREN, "("); break;
case ')': currentToken = new Token(TokenType.RPAREN, ")"); break;
default:
if (Character.isDigit(ch) || ch == '\'' || ch == '/') {
StringBuilder sb = new StringBuilder();
// 提取完整数字/分数
sb.append(ch);
while (pos < input.length()) {
ch = input.charAt(pos);
if (Character.isDigit(ch) || ch == '\'' || ch == '/') {
sb.append(ch);
pos++;
} else {
break;
}
}
currentToken = new Token(TokenType.NUMBER, sb.toString());
} else {
throw new IllegalArgumentException("非法字符: " + ch);
}
}
}
四、项目小结
明确分工与责任意识
项目初期,我们通过讨论明确了彼此的强项:我负责表达式和答案生成以及性能优化,搭档专注表达式答案判定和其余琐碎项。分工后定期同步进度,确保任务不重叠、不遗漏。
沟通机制与冲突解决
使用Git版本控制工具管理代码,避免协作冲突。
建立api文档统一接口要求,协助开发。
互补学习与效率提升
搭档在合作开发的经验弥补了我一张白纸的缺陷,而我在性能优化方面的实践也帮助团队提升了整体效率。
本次合作让我认识到,团队协作不仅是任务的叠加,更是资源整合、沟通协商和信任构建的过程。这些经验将为我未来的团队工作奠定坚实基础。

浙公网安备 33010602011771号