302
社区成员
发帖
与我相关
我的任务
分享最终提交版本共 21 个 .java 文件,分布在 4 个包中:expr(AST 节点层)、parser(解析层)、evaluate(求值层)和默认包(入口)。以下数据为 MetricsReloaded 工具的实际度量结果。
| 类名 | 属性数 | 方法数 | WMC | OCavg | OCmax |
|---|---|---|---|---|---|
ExprValue | 1 | 25 | 83 | 3.19 | 12 |
TermKey | 4 | 9 | 13 | 1.44 | 3 |
EvalVisitor | 3 | 14 | 23 | 1.64 | 4 |
FunctionTable(本体) | 2 | 6 | 7 | 1.17 | 1 |
FunctionTable.FuncDef(内部类) | 2 | 3 | 3 | 1.00 | 1 |
FunctionTable.RecursiveFuncDef(内部类) | 3 | 4 | 4 | 1.00 | 1 |
| 类名 | 属性数 | 方法数 | WMC | OCavg | OCmax |
|---|---|---|---|---|---|
Parser | 2 | 23 | 54 | 2.35 | 11 |
Input | 0 | 2 | 7 | 3.50 | 4 |
Preprocessor | 0 | 1 | 2 | 2.00 | 2 |
| 类/接口 | 属性数 | 方法数 | WMC | OCavg | OCmax |
|---|---|---|---|---|---|
Expr(接口) | 0 | 1 | — | — | — |
ExprVisitor<T>(接口) | 0 | 11 | — | — | — |
NumExpr / VarExpr / NegExpr | 1 | 3 | 3 | 1.00 | 1 |
AddExpr / SubExpr / MulExpr / DerivExpr / PowExpr / ExpExpr | 2 | 4 | 4 | 1.00 | 1 |
ChoiceExpr | 4 | 6 | 6 | 1.00 | 1 |
FuncCallExpr | 3+2常量 | 6 | 6 | 1.00 | 1 |
| 类名 | 属性数 | 方法数 | WMC | OCavg | OCmax |
|---|---|---|---|---|---|
MainClass | 0 | 1 | 1 | 1.00 | 1 |
| 包 | v(G) 平均 | v(G) 总计 |
|---|---|---|
evaluate | 2.30 | 140 |
expr | 1.00 | 45 |
parser | 2.92 | 76 |
| 全项目 | 1.97 | 262 |
总体来看,通过合理的包划分和职责分离,各类内聚程度良好。expr 包中所有类的 OCavg 均为 1.00,是全项目内聚性最好的包。
Input ──► Preprocessor ──► ExtendedParser ──► ExprValue ──► 输出
↑
(解析与求值混合在一个类中)
初始架构完全没有 AST 中间层,ExtendedParser 在解析字符的同时直接构建多项式表示 ExprValue,函数体以字符串形式存储,调用时重新解析。
TermKey 只有 xexp 一个字段,无法支持多变量,难扩展Input ──► Preprocessor ──► Parser ──► AST(Expr) ──► EvalVisitor ──► ExprValue ──► 输出
纯语法解析 ↑ 求值访问者
Visitor接口
(未来可扩展为 DiffVisitor、PrintVisitor 等)
核心变化:
Parser 不做任何计算EvalVisitor 遍历 AST 产生ExprValue,两者解耦得益于第二次作业的重构,HW3 在 HW2 架构基础上直接进行增量开发
HW2 的这次重构几乎是完全重写,问题太多,不如重新设计来得清晰。
重构最大的收益是 HW3 扩展时的开销极低:新增双变量只改了 TermKey 和 ExprValue 中的几个方法;新增求导只加了一个 AST 节点和一个求值方法;新增递推函数只扩展了 FunctionTable 和 EvalVisitor。
ChoiceExpr 中死分支的忽略EvalVisitor.visitChoice 通过 a.subtract(b).isZero() 判断两个 ExprValue 是否相等。其中的死分支不应当进行提前解析,否则会造成超时等性能问题。
在互测阶段,主要采用边界值 + 针对性构造的组合策略:
包括但不限于
x^0(应输出1)、exp((x))^0(应输出1)f{5}(x) 需要展开 4 次递推x^0*y^0 应输出 11, -x阅读被测程序代码,针对性设计用例
基本没有优化,主要想保证代码功能的正确性。
| 作业 | 代码 AI 占比 | 说明 |
|---|---|---|
| HW1 | ~30% | 自己设计核心逻辑和框架,AI 辅助 |
| HW2 | ~40% | AI 参与了 Visitor 模式的骨架搭建 |
| HW3 | ~10% | AI 参与了求导规则实现和递推展开逻辑的整理 |
架构设计规划:在每次迭代前,使用 AI 进行架构方案讨论。AI 能快速给出 Visitor 模式、AST 层的设计建议,质量较高,但对功能的一些复杂的特定约束不了解,需要自己核对指导书。
博客撰写:AI 参与了本篇博客第一部分的数据分析和格式整理,以及部分内容草稿的编写。AI 生成的内容结构合理,但是具体内容必须亲自核实和修改。
互测 bug 寻找:向 AI 描述被测程序的代码结构,请 AI 推测可能的 bug 类型,无法精确定位所有 bug,主要依赖自己针对性的构造数据。
观察互测房间中部分同学的代码风格,很多同学使用ai进行了适当的优化
第一单元的三次作业围绕"表达式处理"这一主题逐步深入,让我对面向对象设计的核心思想有了切身体会。
最深的感受是"架构非常重要"。HW1时图省事写了一个巨大的重量级的解析器,到 HW2想扩展功能时发现根本无从下手,不得不进行一次几乎完全推倒的重构。这次重构花费了大量时间,但换来的是 HW3 扩展很舒服
另外,每次添加新功能前先写测试用例,能在开发过程中立刻发现逻辑错误,而不是等到提交后从通过率来反推问题。
对于第一单元课程的改进建议:
希望第二次作业能够平衡一下开发难度,有实验课类似的形式提供一点具体思路。