oo-第一次总结
oo-第一次总结
第一次作业
题目要求
- 表达式求导,仅包含简单a*x^b项的加减组合
思路
- 将表达式按+-符号分割成各项,对每一项用正则表达式分析出系数a和指数b,求导计算后分别输出。
- 因为是第一次尝试,没有用面向对象的思想。
代码度量
- 类复杂度
![]()
- 方法复杂度
![]()
- 类图
![]()
其中Expression类用于分割表达式,Item类用于分析项并求导输出。
bug分析
- 第一次作业较为简单,强测和互测阶段均未被发现错误。
- 但第一次作业并没有考虑对输出结果进行优化,因此性能分被扣了很多。如果用ArrayList、HashMap等结构进行合并同类项,结果会化简很多。
第二次作业
题目要求
- 表达式求导,在第一次作业的基础上加上了sin(x)和cos(x)项,并允许括号的嵌套。
思路
- 第二次作业较第一次作业发生的质的变化。因为有括号嵌套结构,不能像第一次作业那样单纯地用+-符号做切割,所以对代码进行了重写。
- 为了整理思路,我提取出了这次作业的几个难点:
如何对层层嵌套的括号进行分析
分析处理之后的表达式用什么样的结构储存
储存之后的表达式如何进行求导计算
一个良好的策略,必须能够解决以上三个问题,因此成为我思考的基础。 - 起初我考虑打开括号进行计算,将所有项处理成a*x^b*sin(x)^c*cos(x)^d的形式,之后像第一次作业一样对每项进行求导后分别输出。然而如何展开计算是一个难点,如(a+b)*(c+d)这样的结构,必须要用乘法分配律进行展开,繁琐程度几乎无法接受。即使展开后,如何存储亦是一个问题,若再用一个字符串来存放a*c+a*d+b*c+b*d,想要再展开则需要再做一次分析。如此繁琐的策略显然不能采用。
- 讨论区的许多大佬都推荐递归下降方法,但我实在是太菜了,查阅许多资料之后还是没有一个比较清晰的了解。在ddl即将到达的时候,讨论区战晨曦大佬提出的表达树法为我指明的道路。
- 简而言之,表达式树将表达式处理成树状,其中叶子节点为无法再分割的最小项(如x^a,sin(x)),非叶子节点为计算符号(+-*)。建树使用递归过程,每次寻找不在括号嵌套之内的符号,将表达式分割为左右两块,分别存入左右子树中,然后同理向下。每个节点有以下几个量:符号、表达式本体、表达式求导结果、左子树、右子树。在建树的过程中即可完成求导结果,最后根节点的“表达式求导结果”即为答案。
代码度量
- 类复杂度
![]()
- 方法复杂度
![]()
- 类图
![]()
其中splitExpression类用于递归建树,Expression类为表达式类(初期对表达式做一系列处理),Node类为节点类(树的节点,包含求导方法)。
Expression和Node两个类还是比较美观优雅的,但splitExpression过于庞大臃肿,且不是很“面向对象”。
bug分析
- 因为ddl迫在眉睫,无暇顾及代码风格和结果化简问题,分被扣得很惨。
- 目前已知的bug为表达式开头若是一个+-号,则不能很好的将左子树定为空串。互测中被同质hack了8次...
- 互测阶段试探性地测了一组数据(--0*0*0*sin(x)**-8+-x*x*sin(x)*cos(x)**0),结果成功hack了两位同学。读别人的代码有一定困难,所以很遗憾我并不清楚两位同学具体是什么错误...
第三次作业
题目要求
- 表达式建树,在第二次作业基础上加上了三角函数内的嵌套和WF检查。
思路
- 三角函数内括号嵌套并不算难点,只需将三角函数也视为一种运算符号即可。对于sin(f(x))^a,可将左子树定为指数a,右子树定为f(x)。再施加适当的求导规则即可完成。
- 本次作业的主要难点在于WF检查,根据分析本次WF主要有以下几种;
连续加减号不合法
在不合适的地方加上空白符(如**中)
出现无法识别的符号或单词(如arctan)
括号不匹配(如sin()内表达式必须要再加括号)
我的程序对前三种做了判断(绝大多数可在建树的正则表达式匹配中用else来完成),对于第四种在ddl结束没能来得及进行处理。
代码度量
- 类复杂度
![]()
- 方法复杂度
![]()
- 类图
![]()
由图可以看出整体与第二次作业相差不大,在Expression类中新增了WF检查的方法,splitExpression类中新增了一些else退出语句。
splitExpression类变得更臃肿了...
bug分析
- 强测bug基本上都是三角函数内部表达式非法的情况没能识别出来。
- 互测中被hack了3次,为WA问题。由于时间紧张没能发现bug的由来orz
- 未能发现别人的bug。
重构经历总结
- 完成第二次作业的时间相当紧张,究其原因就在于第一次作业完全无法用于第二次作业中,需要对代码进行整个重写。而第三次作业则相对轻松许多,因为第二次作业基本上已经把表达式求导这一命题的功能完善地差不多了,从类图、类复杂度和方法复杂度可以看出二三两次作业几乎,没有太大差别。
- 我们在完成一个项目时,不仅要考虑这个项目能否完成目前的要求,还要考虑这个项目能否应对将来可能出现的新要求。我们的代码在初版就要足够strong,这样后续的工作才不会手忙脚乱。
心得体会
- 第一单元首次使用面向对象的方法,这三周过得相当紧张。不过经历了一个月的提醒吊胆和捶足顿胸后,基本上已经有了面向对象的思维方法。在将来的作业中争取第一版作业就完成绝大多数工作,减轻之后工作的负担。










浙公网安备 33010602011771号