BUAA OO 第一单元总结

全伟业-22371393 学生 2024-03-23 15:23:33

面向对象课程第一单元总结

前言

oo第一单元的主题是表达式展开,以层次化设计训练为主,本单元涉及三次迭代开发。
本篇博客以介绍三次作业迭代分析的思路和架构为主,包括各方面细节的实现思路和bug分析。

第一次作业分析

任务分析

  • 第一次作业的要求是将表达式括号展开,本次作业的表达式最多包含一层括号。
  • 多项式包括表达式因子,数字因子,变量因子(幂函数因子)
  • 在保证正确性的前提下,尽可能对表达式进行化简,使展开的表达式长度最短。

代码UML类图

img

架构分析

  • 架构设计上主要包括两个层面:表达式解析(递归下降)、表达式展开

  • 整体过程便是先解析表达式,然后对于每一个因子都转化为多项式的形式进行合并和计算,最后输出

具体实现过程

表达式解析

由于题目中表达式第一项、项的第一项会有符号,数字因子本身也可能带有符号,因此在处理语法单元时先采取了replaceAll进行了预处理

  • 由于最多连续三个,情况有限,为了方便直接打表了,打表后就不需要考虑连续多余的符号导致sign的问题了

img

接下来则是对表达式解析,在通过预处理之后解析的过程几乎和训练题一样了,在Lexer中通过next方法顺序访问字符串,根据读取到的字符来分析语法单元

Parser的设计共有三个方法:parseExpr,parseTerm,parseFactor

  • 对于解析表达式和项的方法,大致过程类似,首先对第一项的符号做判断,然后解析第一项(因子),然后通过while循环依次对后面进行解析

  • parseFactor:共有三种情况:表达式因子、数字因子和变量因子

  • 数字因子最容易解析,只需要判断当前语法单元是否为数字,并将当前语法单元实例化为数字因子类的对象,返回该因子即可(数字因子类存储数值和符号)

  • 解析变量因子时,需要判断之后是否存在”^”符号,如果有的话继续访问接下来的语法单元,将指数获取,并返回实例化的变量因子对象(变量因子类存储指数和符号)

  • 解析表达式因子时,以左括号的语法单元判断作为标准,继续递归处理里面的表达式,再重新返回回来时判断是否存在”^”符号,这里由于没有建立表达式因子类,选择直接将乘方展开并化简,返回整体的表达式,后续在介绍输出和化简部分时讲述如何展开乘方

表达式展开

首先分析输出的结果,对于表达式的恒等变形,最终一定是一个多项式的形式
因此引入两个类:Poly类(多项式类)、Mono类(单项式类)
Mono类含有两个成员变量,系数coe和指数exp
Poly类含有ArrayList容器,储存一系列Mono类的对象,并编写多项式加法、多项式乘法的方法
多项式加法比较简单,就是直接遍历两个Poly类的容器,访问他们的每一个单项式
这里合并同类项采取HashMap容器,Key值存指数,Value存系数,
因此在访问Poly类的容器时通过判断HashMap是否存在相同Key值便可做到合并同类项和加入新的项
多项式乘法同理,两层循环遍历乘法,系数相乘,次数相加,同样通过HashMap进行合并同类项

接下来我们需要将Expr类、Term类、还有各种因子类里面写一个方法toPoly(),将该类的对象转化为多项式的形式

  • 对于数字因子类和变量因子类,系数和指数都是显然的

  • 项类转化为多项式(单项式),只需要将ArrayList中存储的因子所转化成的多项式形式再调用Poly类的相乘方法即可

  • 表达式转化同样只需将存储的Term转化成的多项式调用Poly类的相加方法即可

  • 通过这样自下而上递归便能得到最后结果所需要的数据

  • 前文中提到的在parseFactor中展开表达式的乘方,便是将表达式转换为多项式形式(调用toPoly方法),在Expr类再添加一个乘法方法,通过for循环不断调用Poly类的多项式乘法相乘即可

img

表达式输出简化

对于toString中的输出形式也可以稍作化简

  • 单项式系数为0,最终结果为0,
  • 单项式系数为1,则可以省略系数,简化为x^n
  • 单项式系数为-1,则可以省略系数,简化为-x^n
  • x的指数为0,则最终结果只输出系数
  • x的指数为1,则指数部分可以省略,简化为ax
  • 此外其他细节处,比如对于(0)^0=1的情况,在处理乘方时直接特判对于乘方后面的数字为0则该结果为1

方法度量

img


img

第一次作业中Poly类的toString方法分支判断过高,调用方法数也较多,这也为第三次作业的CTLE埋下了伏笔(后续细说)

第二次作业分析

任务分析

本次作业中需要完成的任务为:读入一系列自定义函数的定义以及一个包含幂函数、指数函数、自定义函数调用的表达式,输出恒等变形展开所有括号后的表达式。

  • 迭代处理:指数函数因子类、自定义函数类因子的增加,自定义函数参数的替换处理以及新的表达式计算展开方法

代码UML类图

img

架构分析

  • 新建了Definer类用来定义和替换自定义函数

  • 表达式结构中Factor接口再接入ExpFactor(指数函数因子)、ExprFactor(表达式因子,由于第二次作业中提前处理乘方无法处理自定义函数,故而选择新开表达式因子类方便处理)、FuncFactor(自定义函数因子)

具体实现过程

自定义函数处理

  • 采用的思路是先预处理自定义函数,在Definer类里用两个容器存储,一个存储函数名和对应的函数表达式字符串,另一个存储函数名和对应的形参集合

  • 在解析到自定义函数名时,先解析出函数对应的实参,再将实参传入Definer类中的字符串替换方法,将实参替换形参,然后返回整体的表达式字符串,再对该字符串进行解析

部分代码如下:

img

注意:要规避exp中替换x的方法,再遍历到e的时候要进行特判

表达式展开

在第二次作业中,最小语法单元从系数乘以幂函数的形式,变成了系数、幂函数和指数函数的相乘,因此在第一次作业中采取HashMap中的Key存幂函数次数,value存系数的方法就需要重构

这里我们改用TreeMap存储,由于TreeMap中自带红黑树的顺序,因此采用Key存储字符串(字符串为幂函数和指数函数相乘的字符串形式),这样由于解析方式相同并且转换为字符串都为固定顺序,因此合并同类项便有了正确性保证,再用Value存储Mono类的单项式,这样就在重构的基础上最小的改动了第一次作业的运算方法。

部分代码如下:

img

注意:为了保证指数函数合并同类项的正确性,在解析完之后将指数函数的指数乘入括号内,比如对于exp(x)^2,处理成exp((2*x))

方法度量

img


img

bug分析

强测出现bug:是一个连续多个嵌套的表达式因子乘方展开,于是采取对于只有一项的表达式因子,通过直接计算的形式减少时间复杂度

互测出现bug:CPU_TIME_LIMITED_EXCEED,最初因为本地瞬间就能跑出正确结果而疑惑超时原因,于是通过尽可能的减少方法调用强行优化通过,但依旧为第三次作业的bug埋下伏笔

第三次作业分析

任务分析

  • 本次作业新添了求导功能(但自定义函数表达式不包括该因子)

  • 自定义函数之间可以互相调用

代码UML类图

img

架构分析

  • 和第二次作业相比变化不大,自定义函数互相调用在递归下降算法中完全不用改动就可自行解决

  • 只需新加求导因子的解析和新建类,并在每个因子下新加求导方法

具体实现过程

部分代码如下(此处为Term类求导):

img

方法度量

img


img


img

bug分析修复

  • 第一次作业在强测和互测中均无bug出现,因为第一次作业内容较为简单,架构清晰
  • 第二次作业在互测中出现了CPU_TIME_LIMITED_EXCEED的bug,但是通过不断优化架构中解析的次数,计算上能简则简,能特判就特判的原则强行优化过
  • 这里就要讲一下第三次作业互测出的问题,被hack的点都是CTLE,刚开始还是像第二次作业一样无脑搞,我的第一选择是更改求导思路,参考同学只对单项式形式求导,而不是每个因子添加方法,但没有什么进展,于是通过idea的IntelliJ Profiler,分析时间,发现toString()方法耗时过多,于是发现问题在于在这个方法中,我多次调用了其他类的toString方法,但完全可以一次调用用字符串存下,没必要多次反复调用,节省了大量时间,但这样改后依然有一个循环嵌套求导的数据点过不去,这时候通过IntelliJ Profiler发现CPU Time中单项式求导反复递归占用大量CPU时间,于是又将求导改了回去,便顺利优化完成。
  • 在hack别人的过程中,通常是通过跑评测机,但感觉效果不是很明显,反而是最简单的样例编写更容易hack出结果,更出人意料,其次对于格式上比如优化括号出现的问题也是hack很好的切入点

心得体会与未来展望

  • 第一周突然就上强度,从假期的状态脱离很困难,即使有oopre的基础第一周依然很艰难,不断的讨论和思考才研究好了自己的架构

  • 第二周对于新的表达式展开和计算方式也是想了好久,实现完也是错漏百出,过了中测但自测一堆bug

  • 第三周最为轻松,但是改互测bug的时候很痛苦,把bug改完才撰写这个博客,压着博客提交的ddl很难受,CTLE的bug确实难改,以后在写方法时还是要多注意耦合程度和调用过多的问题,尽量的去简化

  • 每周oo给的压力都好大,思考的煎熬,写代码的折磨和debug的痛苦,直到周末好不容易喘气又进了互测,希望接下来能坚持下去,更加从容地对待这门课程

...全文
40 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

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

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