面对对象第一次博客作业

24371407-程子涵 2026-04-01 21:43:40

姓名:程子涵

学号:24371407

课程:面对对象设计与构造

摘要

本单元三次作业在能力上从括号多项式逐步扩展到含 exp、选择式、自定义函数(HW2),再到求导因子与递推定义的 f(HW3)。架构上采用「递归下降解析 → Node#eval 建树求值 → SymExpr 规范式化简与输出」的主线,将文法代数语义分离。本文结合度量数据、类关系、迭代中的 bug 与互测体会,做一小结。


一、基于度量的程序结构分析

1.1 类级度量

类(HW3)属性/字段(约)方法数(约)代码行数(约)说明
Main0(静态为主)10+190读入、规范化、解析函数体/递推、异常与输出
SymExpr + 内部 MonoKey4 + 320+400规范式 ∑ c·x^k·exp(arg),合并同类项
Expression_Parser + FunctionEnv 等若干25+330解析、Node 求值、f/f' 与递推
Polynomial(遗留)1156HW1 风格,HW2/HW3 中未再作为主路径

HW1 规模更小:Main≈47 行、Polynomial≈172 行、Expression_Parser≈117 行,合计约 330 行量级;HW2 引入 SymExpr 后核心逻辑上移到符号层,解析器行数与圈复杂度明显上升

1.2 方法级示例

方法行数(约)圈复杂度(约)说明
Main.normalizeSignsInPlace256~8连续 +/- 规范化,分支多,曾易写死循环
Expression_Parser.parseFactor(HW3)20+10+x/(/[/exp/f/常数/求导 分发
SymExpr.multiply254~6双重循环合并 MonoKey
SymExpr.toExprString / appendMonomial40+6~8系数、x 与 exp 的必要括号

观察:解析层 parseFactor 随作业迭代分支持续增加(HW3 增加求导、递推环境下的 f),是维护热点;代数层 SymExpr 方法以数据合并为主,分支相对可控。

1.3 OO 度量视角

  • 内聚SymExpr 专责「单项式键 + 系数」的表示与运算;Expression_Parser 专责文法与 eval 闭包,职责边界清晰。Main 在 HW3 中承担了较多输入拆解与合法性判断,内聚略弱,若再迭代可拆出 InputReader / FormatValidator
  • 耦合:解析器依赖 SymExpr 的构造与运算,单向依赖SymExpr 不依赖解析器,依赖方向健康。HW3 中 FunctionEnv 将递推与记忆化封装在解析包内,避免 Main 直接操作备忘录,略有改善。
  • WMC/CBO:未用插件逐类填表;直观上 Expression_Parser 的 WMC 最高,与「因子种类多」一致;CBO 上 Main 与 Expression_ParserSymExpr 相连,属预期。

1.4 类图

下列为示意类图(手工整理,非 IDEA 一键逆向)。表达「谁依赖谁」即可。

classDiagram
    class Main {
        +main()
        -normalize()
        -parseFunctionDefinition()
    }
    class Expression_Parser {
        <<interface>> Node
        +parseExpressionNode()
        -parseFactor()
    }
    class SymExpr {
        -Map terms
        +add() multiply() pow()
        +toExprString()
    }
    Main ..> Expression_Parser : 创建
    Main ..> SymExpr : 输出
    Expression_Parser ..> SymExpr : eval 得到
  • Main:负责 IO、去空白、符号规范化,以及 HW3 的递推/函数体读入。设计意图是入口薄、异常统一
  • Expression_Parser:递归下降;Node 用 eval(SymExpr argForX) 统一表达式因子函数体内 x 的代入,避免为每种因子写重复遍历。
  • SymExpr:不可变(逻辑上)规范式;MonoKey 绑定 x 指数与 exp 内层参数,乘法对应指数相加、exp 内层相加,契合作业恒等式。
  • Polynomial:HW1 遗留;HW2 起主路径已迁移到 SymExpr,保留便于对照或工具类使用。

优点:解析与语义分层,扩展因子时在 parseFactor/eval 增加分支即可。
缺点Expression_Parser 单类偏长;HW3 后求导与递推与通用因子耦合在同一类中,若再增加语法,可考虑按因子类型拆子解析器(需权衡作业规模)。


二、架构设计体验:三次作业迭代

2.1 迭代脉络

作业新增能力架构做法
HW1表达式展开为多项式,x^k、括号、乘方Expression_Parser → PolynomialHashMap<指数, 系数>),快速幂 pow
HW2exp(因子)[(A==B)?C:D]f(因子)f(x)=…引入 SymExpr + MonoKey;解析返回 Node,选择式用 (A-B) 是否恒为 0 判定;f 对实参求值后代入;可选 functionEvalMemo 避免重复展开
HW3求导因子、递推定义 f(n)、更多格式约束FunctionEnv 持有普通 f 与递推式evalRecursiveFunction + RecurrenceKey 记忆化parseDerivativeFactor;非法时输出 Wrong Format(与 HW2 静默退出策略不同,以作业要求为准)

2.2 重构与对比

  • 从 Polynomial 到 SymExpr:不是小改,而是表示模型的更换——从「单变量幂」到「幂 × exp(参数)」的直和。重构动机是 HW2 文法已无法用单一 HashMap<Integer, BigInteger> 表达。
  • 解析器:HW1 直接返回 Polynomial;HW2 起返回 Node延迟求值到 eval,使 f 的实参与定义体解耦。
  • 体验:前期一次性把 SymExpr 运算写全,后期 HW3 主要在解析器上堆分支;若早预判 HW3,可把「函数环境」抽象得更早。

2.3 自定迭代情景:若增加 ln(exp中的内容)

  • 可复用SymExpr 的合并思想、Node 求值链、括号与输出规则的模式。
  • 需新增:文法因子 ln(Factor);语义上 ln 与 exp 是否可化简为多项式取决于课程定义——若只允许形式展开,需在 SymExpr 增加新因子类型或扩展 MonoKey,改动面大于加 +
  • 结论:当前「规范式 + 单项式键」对线性指数环友好;对 ln 这类需新等价关系的运算,应预先设计可扩展的因子枚举,避免继续膨胀 parseFactor 的 if-else。

三、基于自身程序的Bug 分析

3.1 典型问题与定位

特征问题表现涉及类/方法原因设计层面改进
预处理中 */^ 后跟 -长时间运行或 TLEMain.normalizeSignsInPlace某些分支未推进 index,对 -1*-2 类串死循环规范循环不变式:每次迭代要么缩短串,要么 i++;对边界写单测
选择式/等价判定WAExpression_Parser.evalChoice / SymExpr.subtract恒等判定依赖化简后为零MonoKey 合并或 exp(0) 未消干净会导致误判统一「零元」与 exp(0)=1 的入口,避免多处特判不一致
HW3 格式RE 或 Wrong FormatMain 读入与校验行数、关键字、禁止出现在函数体中的结构等与 HW2 不同按文档单独分支,避免复用 HW2 的「静默退出」逻辑到 HW3

3.2「出过问题的方法」与「稳定方法」对比

  • normalizeSignsInPlace:行数不多,但分支嵌套多、圈复杂度高,易漏 index++
  • SymExpr.add:逻辑以 merge 为主,线性、模式重复,出错率低。

降复杂度:对符号规范化可拆成「删除 ++」「折叠 +-」等小函数;解析器可对 parseFactor 用 switch/表驱动(若 Java 版本允许)或按首字符分派到私有方法,减少单方法内的分支数。


四、互测中发现他人问题的策略

  1. 按文法分层:空串、单字符、仅常数、仅 x、深层括号与 exp 嵌套。
  2. 结合代码结构:若对方用字符串拼接输出,测项顺序与参考是否仅差排列(代数等价但字符串不同——以课程判题方式为准);若对方 exp 合并弱,测 exp(a)*exp(b) 与 exp(a+b)
  3. 边界与非法输入n 与函数行是否匹配、互测 cost 与长度限制(若平台有)。
  4. 有效性针对性用例比随机 fuzz 更能一击致命;阅读被测代码的 parseFactor 顺序与特判 性价比高。

五、优化工作与正确性

5.1 做过的优化

  • exp(0) 消去、同类项合并,减少输出与中间项规模。
  • SymExpr.pow 对单项快速路径 + 一般情况二进制幂,控制次数。
  • HW2 对同一 f(实参) 的记忆化(若启用),减少重复展开。

5.2 与简洁性、正确性

  • 优化多在 SymExpr 不变式内完成,不破坏「先规范再输出」的流程,回归时重点测乘法与 exp 合并即可。
  • 风险在于过度特判导致漏情况;缓解方式是保留对照实现(如 CheckEquiv 小工具)与课程样例全集回归

六、大模型使用说明

以下比例为个人主观估计,表示「相关模块里有多少思路或代码片段曾参考过大模型的建议」,非精确统计。整体保持辅助为主、核心手写,故占比刻意不报高。

作业正确性相关(约)性能/优化相关(约)说明
HW1约 18%约 15%递归下降与 Polynomial 主体为自己实现;曾用模型核对 BigInteger 用法、括号与符号处理的边界说法,少量语句经提示后改写。
HW2约 12%约 18%SymExpr / MonoKey 与解析器架构与绝大部分代码为手写;曾让模型帮忙梳理 exp 合并、exp(0) 与选择式恒等判定的注意点,以及 toExprString 里括号规则的对照检查,未整文件生成。
HW3约 18%约 13%递推与求导分支多,用模型辅助阅读题意、把递归与记忆化拆成步骤说明,再在 IDE 里自己落代码;性能上仅讨论过记忆化是否必要、未依赖模型做复杂改写。

代码之外的使用:整理本单元博客提纲、把长句改顺、英文类名/术语核对;偶尔问「递归下降遇到某类因子该怎么接」这类概念题。效果上能省一点查资料时间,但公测与强测是否通过仍依赖本地调试与对拍,模型不能代替逐条用例验证。

声明:未使用大模型批量生成整份作业工程;核心逻辑与 bug 修复过程以自己调试为主。


七、心得体会

  • 抽象的重要性:HW1 的「按指数存系数」在 HW2 必须让位于「带 exp 参数的单项式键」,过早优化成多项式会阻碍扩展。
  • 测试与预处理符号规范化看似小事,却是线上死循环高发区;小函数 + 单测比事后调试更省时间。
  • 互测:提交前多做边界与题面约束自查;等价输出与字符串判题的差异让我更注意题目说明与评测方式

八、对第一单元课程的建议

  • 在第二次作业前提供更明确的「规范式定义」与输出排序规则,减少互测争议。
  • 对 cost、长度 等约束给出可运行的小工具或公式样例,降低「以为合法实则非法」的挫败感。
  • 研讨课可增加**「典型错误输出」**共性问题汇总(匿名)。

九、思考题(选做:输入检查与 Cost)

9.1 如何检查输入是否符合要求?(空格、连续符号等)

  1. 空白:读入后剔除空格与 \t(若题目允许),或按规则拒绝非法字符。
  2. 连续符号:在 StringBuilder 上扫描,将 +++--+-- 及 */^ 与紧随其后的 +/- 按规则折叠;关键是为每种模式定义是否移动游标,避免死循环。
  3. 合法性:词法(字符集)、语法(递归下降能否读完且无多余字符)、语义(如 HW3 禁止在函数体出现某些结构)分层;非法时按作业要求输出或退出。

9.2 如何精确计算合法输入的 Cost?

  1. 以课程 PDF 为准,对表达式、因子、函数实参等递归定义代价。
  2. 实现:对 AST 后序遍历,每个节点返回子树 cost,按规则合并;注意 f(实参)exp 嵌套选择式 是否单独计费。
  3. 校验:与同学独立实现对拍,或对官方样例手算核对。
...全文
87 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

302

社区成员

发帖
与我相关
我的任务
社区描述
2026年北航面向对象设计与构造
java 高校
社区管理员
  • 孙琦航
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧