BUAA-OO-Unit1表达式化简单元总结

王怀阳-22373568 王怀阳-22373568 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区

@

目录

  • 写在前面
  • 一、三次具体的作业
  • 1.1 第一次作业
  • UML类图
  • 类功能介绍
  • 一些细节
  • 1.2 第二次作业
  • UML类图
  • 类设计考虑
  • 一些细节
  • 1.3 第三次作业
  • UML类图
  • 类功能介绍
  • 一些细节
  • 二、度量分析
  • 2.1 Design Code Smells
  • 2.2 Implementation Code Smells
  • 2.3 Type Metrics
  • 2.4 Method Metrics
  • 三、设计体验
  • 四、Bug分析
  • 五、互测策略
  • 六、优化分析
  • 七、心得体会与未来方向

写在前面

  • 本次作业共包含三次迭代:
    • 第一次实现了包含加、减、乘、乘方、单层括号的表达式处理
    • 第二次实现了嵌套括号处理、自定义函数处理、指数函数处理
    • 第三次实现了表达式求导与自定义函数嵌套
  • 作业过程中的主体思路为递归下降算法,还包含有诸如字符串替换、SOLID法则的一些小细节,具体处理过程如下:
    • 预处理:对表达式字符串进行预处理。
    • 构造语法树:采用递归下降算法。
      • 第一层递归:分析表达式中的项(以+、-为分隔)。
      • 第二层递归:分析项中的因子(以*、^为分隔)。
      • 第三层递归:分析因子:
        • 表达式因子:若为带括号的表达式或指数函数,回到第一层。
        • 自定义函数因子:应用字符串替换后,回到第一层。
        • 求导因子:调用求导方法后,回到第一层。
        • 幂函数/常数因子:返回相应多项式给第二层。
    • 性能优化:进行相关的化简处理。

一、三次具体的作业

  • Unit1的主题为表达式化简,需要处理含有加、减、乘、乘方、括号、指数函数、自定义函数、求导等功能的表达式,并进行展开和计算。

    1.1 第一次作业

    UML类图

    第一次作业类图

    类功能介绍

  1. Main类是程序的入口,负责初始化Preprocessor和Parser对象,并调用它们的方法。
  2. Preprocessor类负责预处理输入的字符串,包括删除空格,处理加减符号等。
  3. Parser类负责用递归下降算法解析预处理后的字符串,将其转换为Poly对象。
  4. Unit类表示单项式,在第一次作业中的形式为$ax^b$。类内包含了单项的各类操作,如运算操作、重载的构造方法、toString的重写等。
  5. Poly类表示多项式,在第一次作业中的形式为$\sum(ax^b)$,数据结构为HashMap<Integer, Unit>。类内包含了多项式的各类操作。

    一些细节

  • 不同于大部分同学类的结构,我的此次设计中只有5个类,这是因为我并没有设计继承自Factor的各种因子,而是将所有因子用单项式$ax^b$实现。实现过程中用到了重载的构造方法。

  • 同时,我也没有设计Expr、Term类,而是全部以Poly类实现相应内容。这样设计的优点是易扩展(尤其是在第三次作业),缺点是思路比较面向过程。

  • 此外,我并没有创建Lexer类,而是将其功能直接在Parser类中通过下面所示的方法实现。我认为这样相较于Lexer.next()会更加好理解,也能体现词法分析本身便是语法分析的一部分。

    //以+、-为分割来切割表达式中的项
    //跳过嵌套的括号
    public List<String> splitExpression1(String expression) {
          List<String> tokens = new ArrayList<>();
          StringBuilder currentToken = new StringBuilder();
    
          int parenthesesCount = 0;
    
          for (char c : expression.toCharArray()) {
              if (c == '(') {
                  parenthesesCount++;
              } else if (c == ')') {
                  parenthesesCount--;
              }
    
              if (parenthesesCount == 0 && (c == '+' || c == '-')) {
                  tokens.add(currentToken.toString().trim());
                  currentToken = new StringBuilder();
                  tokens.add(String.valueOf(c));
              } else {
                  currentToken.append(c);
              }
          }    
          tokens.add(currentToken.toString().trim());
          return tokens;
      }
    

1.2 第二次作业

UML类图

第二次作业类图

类设计考虑

  1. Main类与Parser类中新增了负责处理自定义函数相关的字符串替换方法。
  2. Unit类的基本表示形式变为$ax^b*exp(Poly)$。类内重写了equals()和hashcode();与此同时Poly类的数据结构变为HashMap<BigInteger, HashMap<Poly, Unit>>,类内也重写了equals()和hashcode(),并且与Unit类中的同名方法存在递归调用的关系。

    一些细节

  • 在第二次作业中,使用两层HashMap来实现多项式有助于通过hashcode()和equals()方法快速判断并合并同类项。然而,这种方法本质上是利用容器的数据结构层次化来替代了抽象结构的层次化,且只适用于当前的情景。如果多项式的需求发生变化,例如增加三角函数因子或对数函数因子,那么整个项目可能需要进行大规模的重构。
  • 这表明,这次作业的完成更多地面向过程而非面向对象。在这个阶段,笔者选择了一种比较省时省力的方式来完成这次迭代,但这其实不是一个好的选择。因为在软件开发中,我们应该尽可能地面向对象,以提高代码的可读性和可维护性,同时也能更好地适应需求的变化。

    1.3 第三次作业

    UML类图

    第三次作业类图

    类功能介绍

  1. 在Poly类和Unit类中新增了求导的实现(即getDerivation()方法)。实现过程中存在递归调用。

    一些细节

  • 由于之前的架构设计得较为清晰,所以本次作业我只花费了20分钟便完成。

二、度量分析

基于DesigniteJava,对经历三次迭代后作业的分析如下:

2.1 Design Code Smells

Type NameCode Smell
MainUnutilized Abstraction
PolyCyclic-Dependent Modularization
PolyInsufficient Modularization
UnitCyclic-Dependent Modularization

表格列出了类中的代码异味:

  • Main类中发现了'Unutilized Abstraction',即"未使用的抽象",表示有未使用的接口或抽象类。这是因为在重构过程中,我将Lexer类移除,其功能由Parser类接管,但相关注释未被删除。
  • Poly和Unit类中发现了'Cyclic-Dependent Modularization',即"循环依赖模块化"。这是因为在使用递归下降算法时,我没有定义多种因子及其继承、接口关系,而是用多项式和单项式处理,导致模块间紧密耦合和循环依赖。
  • Poly类中发现了'Insufficient Modularization',即"模块化不足"。这是因为在设计和实现代码时,我没有充分考虑到模块化,而是一味根据新需求堆加新方法,导致功能和逻辑没有被合理划分和封装到独立模块中。
  • 通过查阅资料我发现,'Unutilized Abstraction'会导致代码冗余和混乱,增加理解和维护成本;'Cyclic-Dependent Modularization'会增加代码复杂性,降低可读性和可维护性,增加修改和测试难度;'Insufficient Modularization'会导致代码结构和逻辑混乱,降低可读性和可维护性,影响代码的复用性和扩展性。虽然在三次迭代中,这些异味可能影响不大,并且异味出现的主要原因是我没有花费时间维护类的模块化,但在实际迭代开发中,这些异味可能引发问题,值得警惕。同样的道理在下一个异味分析中适用。

    2.2 Implementation Code Smells

    Type NameMethod NameCode Smell
    ParsersplitExpression1Complex Conditional
    ParsersplitExpression2Complex Conditional
    ParserparserTermMagic Number
    ParserparserTermMagic Number
    ParserparserFactorComplex Conditional
    ParserparserFactorMagic Number
    ParserparserFactorMagic Number
    PolyoneBracketComplex Method
    PolytoStringComplex Conditional
    PolytoStringComplex Method
    PolytoStringMagic Number
    PolytoStringMagic Number
    UnitequalsLong Statement
    UnittoStringComplex Method

表格展示了类的方法中发现的实现代码异味:

  • Complex Complex Conditional:这种代码异味在Parser类的splitExpression1、splitExpression2和parserFactor方法,以及Poly类的toString方法中被发现。它表示这些方法中存在复杂的条件语句,包含多个条件判断。这会导致代码的阅读和理解变得困难,同时也增加了测试的复杂性。
  • Magic Number:这种代码异味在Parser类的parserTerm和parserFactor方法,以及Poly类的toString方法中被发现。它表示这些方法中存在魔法数字,即直接使用的数字常量,而没有给它们一个有意义的名字。这会导致代码的可读性和可维护性降低,因为其他人可能不清楚这些数字的含义和用途。
  • Complex Method:这种代码异味在Poly类的oneBracket和toString方法,以及Unit类的toString方法中被发现。它表示这些方法的复杂性较高,包含多个逻辑分支和循环结构。这会导致代码的阅读和理解变得困难,同时也增加了测试的复杂性。
  • Long Statement:这种代码异味在Unit类的equals方法中被发现。它表示这个方法中存在长语句,一行代码的长度超过了推荐的最大长度。这会导致代码的阅读和理解变得困难。

2.3 Type Metrics

Type NameNOFNOPFNOMNOPMLOCWMCNCDITLCOMFANINFANOUT
Main001120200-1.002
Parser108815032000.013
Poly10202026472000.132
Preprocessor1066449000.020
Unit40191916638000.032

表格展示了不同类的度量指标,其中不太好的指标主要有:

  • 加权方法数量(WMC):Poly类型的WMC值最大,为72,这意味着Poly类型的复杂性较高,需要更多的精力去理解和维护。
  • 代码行数(LOC):Poly类型的LOC值最大,为264,这表示Poly类型的代码长度较长,需要更多的时间来理解和维护。
  • 类内方法相似度(LCOM):Poly类型的LCOM值最大,为0.1,这可能意味着Poly类型的方法之间的相似度较高,存在一些重复的代码。
  • 输入耦合(FANIN):Poly和Unit类型的FANIN值最大,为3,这表示这两个类型被其他类型引用的次数较多。

2.4 Method Metrics

Type NameMethod NameLOCCCPC
Mainmain1821
ParserParser311
ParsersplitExpression12351
ParsersplitExpression22351
ParserparserExpr1741
ParserparserTerm2041
ParserparserFactor2561
ParsersplitArgs2051
ParserCustomFunc1621
Polyequals1341
PolyhashCode310
PolyPoly210
PolyPoly611
PolyPoly511
PolyPoly311
PolygetDerivation1440
PolyoneBracket2380
PolygetHashMap720
PolyaddUnit1931
PolyaddPoly1451
PolysubUnit2131
PolysubPoly1451
PolymutPoly1861
PolyexpPoly1731
PolygetGcd2060
PolysetGcd931
PolyisEmpty720
PolydelNull620
PolytoString40110
PreprocessorPreprocessor311
PreprocessordelBlank310
PreprocessorhandleAddSub1110
PreprocessorhandletheFirst1440
PreprocessorhandleMinus310
PreprocessorgetResult710
Unitequals1031
UnithashCode310
UnitUnit412
UnitUnit614
UnitUnit311
UnitUnit311
UnitUnit410
UnitUnit513
UnitgetDerivation2540
UnitsetFuncexp411
Unitadd411
Unitsub411
Unitmult1641
UnitgetCoe310
UnitgetExp310
UnitgetFuncPoly310
UnitBaseKey310
UnittoString54120
UnitgetFuncExo310
  • 表格展示了不同类的方法的度量指标,从整体上看,这些方法的规模较小,最多不超过54行(Unit类型的toString方法),显示这些方法的结构良好,易于理解和维护。同时,方法的圈复杂度大部分在6以下,这进一步说明了这些方法的逻辑较为简单。
  • 然而,有几个方法的圈复杂度较高,例如Poly类型的oneBracket方法和toString方法,以及Unit类型的toString方法,它们的圈复杂度都超过了10。这可能是由于这些方法中存在一些复杂的条件判断和循环结构,使得这些方法的逻辑变得较为复杂。
  • 此外,Parser类型的splitExpression1、splitExpression2、parserExpr、parserTerm和parserFactor方法的圈复杂度也较高,这主要是由于这些方法中使用了递归下降的实现方式,在过程中需要进行类型判断,从而增加了方法的复杂度。

三、设计体验

  • 在三次迭代中,我的架构没有经过太大的变化,大部分的架构在第一次的作业中就已经成型。这在我三次大差不差的UML类图中就可以看出来。
  • 唯一产生较大改变的是Poly类中在第二次作业从单层的HashMap存储Unit变为双层HashMap嵌套的结构。而在第二次作业的分析中我也提到,这种方法本质上是利用容器的数据结构层次化来替代了抽象结构的层次化,且只适用于当前的情景。如果多项式的需求发生变化,例如增加三角函数因子或对数函数因子,那么整个项目可能需要进行大规模的重构,比如说把HashMap的嵌套层次加深,亦或者直接采取ArrayList的容器来存储并新写一个合并同类项的方法。

四、Bug分析

  • 比较悲惨的是,我的程序在第二次作业和第三次作业中都出现了Bug,且Bug都是format error,即为了得到更多性能分去掉了不应该去的括号,第二次作业中是出现了exp(-x)的错误,第三次作业中重写了去括号判断的方法,结果写得不严谨,又出现了exp(x*exp())的错误。
  • Bug出现的原因还是没有仔细阅读指导书中的对表达式的形式化定义以及写程序时草率的心态。因为如此粗心而丢掉了不该丢的分数,我也感到追悔莫及。我应该端正自己对待作业的心态。

五、互测策略

  • 我的测试策略是将评测机与人工简化数据相结合。这种方法效率较高,尽管有时会发现一些同学的程序由于Cost限制而无法进行内存超限(MLE)和时间超限(TLE)的攻击,但这种方法发现的Bug仍然具有较高的有效性。

六、优化分析

  • 在第二、三次作业中,我实现了以下优化:
    1. 正项提前
    2. exp 内表达式是否可以去括号
    3. exp 内表达式是否可以提取公因数到指数函数的指数部分
  • 现在看来,因为去括号导致出现了Bug,还真是有些得不偿失。

七、心得体会与未来方向

  • 我在第一单元碰到的最大困难还是在对于递归下降算法的理解上。这导致我在第一次作业上花费的时间比第二、三次作业加起来还久,也是在周三的上机后才逐渐认识到这一算法的思想。课程组可以考虑在寒假给同学们布置对该算法的预习以及相关的训练任务,不要让同学们因为对算法不理解而不是对面向对象思维不理解而感到沮丧。
...全文

相关推荐

从注意力到行动 pdf Agent

从注意力到行动 pdf

言之。

同步电机与构网型变流器的频率稳定特性及多时间尺度交互机理研究(Simulink仿真实现)

内容概要:本文系统解读了一套完整的多机器人路径规划(MAPF)MATLAB代码库,重点围绕优先级规划算法展开,涵盖从地图解析、拓扑构建、路径规划、冲突消解到可视化呈现的全流程。文章提出四层架构设计:几何层负责地图解析与栅格化;拓扑层构建四邻域图与Petri网模型;调度层实现基于动态边权膨胀的优先级路径规划与时间同步冲突消解;呈现层完成动画与视频输出。核心创新包括动态边权膨胀机制避免硬封锁导致的虚假不可解、节点预留历史实现高效冲突查询、几何惩罚驱动的局部同步提升效率,以及Petri网双阶段优化支持解的存在性与最优性分离判断。框架兼顾实用性与可扩展性,适用于大规模智能体场景。; 适合人群:具备MATLAB编程基础,从事机器人、自动化、智能交通或路径规划相关研究的科研人员与研究生。; 使用场景及目标:① 学习并复现多机器人无冲突路径规划的经典与先进算法;② 掌握动态边权膨胀、时间同步、冲突消解等关键技术的工程实现;③ 借助模块化框架进行算法改进与性能优化,支撑仓储物流、无人机编队等应用场景的仿真研究。; 阅读建议:建议按文中所述的数据流顺序逐步阅读,重点关注各模块接口设计与算法取舍逻辑,结合提供的完整资源进行代码调试与实验验证,深入理解“空间规划-时间消解”分离的设计思想。

程序猿鑫

huashu-motion 让编码 Agent 直出商业宣传片-一个专门给 Claude Code、Codex 等工具使用的 Skill

huashu-motion 一个专门给 Claude Code、Codex 等工具使用的 Skill。它不跑黑盒视频生成模型,而是让 Agent 直接用代码绘制动画,成片就是一个网页。 项目最大的特色是“先写谱再拍片”,Agent 能写纯文本乐谱配合乐器采样在本地配乐,并精准对齐每帧音效。在浏览器里按 C 键点击画面就能直接留批注,Agent 会定位源码只改动那一处,也可以在右侧表单手动调参。 它还内置了从顶级发布片提炼的 20 种运动语言、自动化质检与并行渲染机制,小模型同样能做出高级感。

一叶知秋yyds

YOLO26算法校园作业场景锚目标检测+训练好的模型+103张数据集+pyqt可视化界面.zip

附训练好的 YOLO 权重与 PyQt 可视化检测界面,页面底部含可视化效果预览,毕设/课设开箱即用。 【数据集概况】 · 检测类别(中文):[锚(anchor)] · 训练集:90 张 · 验证集:9 张 · 测试集:4 张 · 总计:103 张 该数据集聚焦校园作业场景,通过精准标注手写文字中的锚元素,为教育场景下的文本识别与作业分析提供高质量数据支撑,助力教学评估与学生书写质量研究。该数据集训练集占比90%,验证集与测试集分别占9%和4%,符合目标检测任务数据划分的常规比例,训练集规模充足以保障模型学习能力,验证集与测试集合理分配用于模型性能评估,确保数据分布科学合理。该数据集标注工作严格遵循规范,所有锚元素的边界框标注精准,标注内容与... 【训练曲线与评估图】 【模型训练配置】 参数 | 值 模型 | yolo26n 训练轮数 | 100 epochs 输入尺寸 | 640x640 批次大小 | 24 优化器 | auto 初始学习率 | 0.01 训练设备 【关键指标汇总】 训练了 100 个 epoch,最终轮指标: 指标 | 数值 mAP50 | **0.6351** mAP50-95 | 0.1958 Precision | 0.6436 Recall | 0.5714 train/box_loss | 1.6649 train/cls_loss | 1.3181 val/box_loss | 2.3156 val/cls_loss | 2.5145 【训练过程分析】 100 轮训练后 mAP50 仅 0.6351,模型收敛不充分。Loss 曲线前期下降后进入平台期,mAP 提升缓慢。mAP50-95 为 0.1958,定位精度同样偏低。 【模型性能评估】 Precision 0.643... 使用场景:毕业设计、课程设计、深度学习入门、工业场景落地验证。

guo_xiao_xiao_

基于扩展卡尔曼滤波(EKF)的激光雷达与雷达异构传感器融合算法研究(Matlab代码实现)

内容概要:本文系统解读了一套完整的多机器人路径规划(MAPF)MATLAB代码库,聚焦于优先级规划算法的工程实现。该框架将求解过程划分为几何、拓扑、调度与呈现四个解耦层次,实现了从地图解析、四邻域拓扑与Petri网建模,到基于优先级启发式的路径规划、时间同步冲突消解,再到动画可视化的全流程闭环。其核心创新在于“空间规划-时间消解”分离的设计思想:通过动态边权膨胀策略取代传统硬封锁,允许后续智能体在必要时穿越先前路径,提升狭窄环境下的规划成功率;利用节点预留历史机制实现高效的冲突查询,并结合几何惩罚机制在规划阶段预测潜在冲突,显著提高时间同步效率。框架还支持Petri网整数规划求解路径,增强了算法的完备性与优化潜力,整体具备良好的可复现性、可扩展性与教学价值。; 适合人群:具备一定编程基础和机器人路径规划基础知识,从事相关科研或工程应用的研发人员,尤其是高校研究生及自动化、人工智能方向的技术人员。; 使用场景及目标:①应用于仓储物流、无人机编队、自动驾驶等多智能体协同场景中的路径规划问题;②为MAPF算法的教学、实验与性能评估提供可复现的MATLAB实现参考;③支持对优先级规划、冲突消解机制、Petri网建模等关键技术进行二次开发与优化验证。; 阅读建议:建议读者按照文中所述的数据流顺序逐步学习,重点关注各模块之间的接口设计与信息传递机制,结合提供的可视化工具调试与观察算法行为,深入理解“空间规划-时间消解”分离的设计思想,并可在实际应用中尝试调整惩罚权重、同步策略等参数以优化性能。

李锦科研
评论
收藏 已收藏
分享
返回顶部