BUAA_OO_2020_第一单元作业总结

BUAA_OO_2020_第一单元作业总结

第一单元的主题为表达式求导。

第一次作业

度量分析:

BUAA_OO_2020_第一单元作业总结_第1张图片
BUAA_OO_2020_第一单元作业总结_第2张图片

  • 架构:三个类,分别是主函数,IO处理类:用于将表达式解析,多项式Poly类:负责管理每一项,并继承了求导,打印等功能.
  • 由于这次作业本身功能简单, 完成的思路和面向过程的设计方式类似,但尽可能地采用了一些面向对象的思想.
  • 从统计图中可以看到,printItem方法的三项复杂度都很高.v复杂度是由于有多项特判的输出,其余两项的耦合也较严重. 其实,更好的设计思路是将每一项单独建立一个类,并设置自己的printitem函数,而非在顶层类直接特判,调用,输出. 但是由于当时觉得太繁琐,就没有那样做.但总体而言,即使统计的复杂度高,代码本身的逻辑非常清晰,可以形式化逻辑推理输出模块功能的正确性.

自己 or 发现同学的bug的策略

自己的作业在中测,强测,互测(以下简称三测)中没有发现任何bug

关于互测部分:

首先,由于自己写了完善的自动评测脚本,先将所有同学的代码跑了一中午,一些明显的bug就已经被发现了,其中一位同学的问题其实很严重,他的算法未能成功截取用空格分开的两个符号,会忽略掉第一个.

随后,自己大致审查了代码.但是客观来讲,本room的人既然强测基本全对了,代码中很难找到明显的问题,除非自己详细一行一行缕清逻辑, 对每一步关键算法都分析边界和可能的未考虑清楚的bug.自行审查代码没有任何收获.

第二次作业

第二次作业中同时支持了三角函数,和幂函数,可以将二者抽象为一类然后同时实现求导,打印等操作已经是明显的暗示了.

在本次作业中,整体的实现思路是将输入的表达式手动解析为用+-分离开的一项一项的式子,然后根据*分开,送进工厂模式进行构造.

度量分析

本次作业中,面向对象的样子至少已经装出来了,采用了多个类,将cos,sin,幂函数,项,表达式全部新建类,用工厂模式批量生产这些类.由于这些类并非管理了共同的数据(至少很难抽象),因此采用了一个接口Calcable用来描述他们共同的行为(例如可以求导,可以打印)代码UML如图

代码分析如图:

BUAA_OO_2020_第一单元作业总结_第3张图片
BUAA_OO_2020_第一单元作业总结_第4张图片

从图中可以看到,各类方法基本都符合要求,除了打印函数的逻辑复杂度依旧过高,但正如之前所言,这种复杂度个人认为是不好避免的.

在性能方面,由于自己的算法能力并不强,并没有采用不少dalao使用的诸如dfs,剪枝等策略进行优化,而是只进行了一些非常明显的特判,(相当于是他们的一层dfs).这直接导致白给了性能分的一大部分.

自己 or 发现同学的bug的策略

本次作业没有在三测中发现任何bug. 中测开始几次出现了几次莫名其妙的RE,最后排查发现仅仅是exit函数的返回值写错了.

在互测中,读代码依旧没能发挥用处,所有的bug都是评测脚本跑出来的.一位同学特判了一些函数值直接为0 的情况,但是在输出的时候忘记输出了乘号,导致承担了全组99%的刀.

第三次作业

第三次作业属于比较难的一次,我甚至觉得接近我程序设计能力的上限了.

  • 整体架构和主要算法:

    相比第二次,直接进行了重构,但是保留了很多思想.例如都是将每个函数新建一类,在类中管理诸如打印,求导,指数和底数(这里指sin,cos的括号内内容),同样是用Term管理项,用Expr管理表达式

    所有的函数用Calcable接口进行抽象, 由于某些复杂的原因 ,我并没有将Term和 Expr应用Calcable接口,而是在外面包装了一层括号类,其内容可以是Expr类型.而括号类则应用了Calcable接口.这样,所有的元素都可以集中调用了.之所以没有应用,是因为设计阶段觉得Term和Expr的很多行为和函数其实并不相似,同时考虑了诸如Expr本身不需要输出括号,但是嵌套在函数内时却有可能需要括号等原因,因此并没有将Expr和函数放在相同地位的位置.但是后来发现其实这种"包装"并不必要.

  • 性能

    本次作业我也采用了被广泛采用的"边解析边下沉"的方式,构造递归层层进行解析处理. 先替换出括号内的内容,然后正则匹配,再将对应的括号内的内容递归到下一层进行解析即可. 一些同学提到了所谓"先去掉不必要的括号才能避免TLE",但是对于我而言,没有进行任何先去括号的优化,依旧得到了数量级的时间冗余.这部分原因有待我进一步看一下那些同学的代码进行分析,查找二者算法的不同处.

    在化简上,我并没有做非常"复杂"的工作,不过是基本的"合并同类项,合并函数,去掉不必要的输出".但从性能分的表现来看还算略成功,毕竟那些高级的三角化简技巧在真正出现复杂嵌套的测试点里,除非特意构造,否则出现的概率其实并不大.

分析如图:

BUAA_OO_2020_第一单元作业总结_第5张图片
BUAA_OO_2020_第一单元作业总结_第6张图片

如图中可以看到,构造函数的工厂方法明显复杂度超标.在写的时候,我就认识到了自己写了四段一模一样的构造代码,且险些违反method length的checkstyle , 但是由于已经写好了,就不想重构了. 与此同时,工厂方法实际承担的任务过重了,未经任何处理的表达式项被扔给了工厂方法,要求它识别函数类型,识别有无括号,并处理包装函数内层的括号,返回识别出的函数并递归至其内部的嵌套(递归又要传递很多需要在本层解析出的参数,例如内部有多少个括号,括号的类型等),导致工厂方法很臃肿.归根揭底, 这是因为写之前没有划分好类的职责功能.这一点,是本次作业的一大败笔.

虽然还有一部分超标,但是总的而言,至少是受限于笔者的水平,我认为那些复杂度难以避免.比如一些类,Term,要调用各种各样的因子,这注定会导致耦合度爆炸.添加类的时候,要逐一判定能否合并同类相,而合并同类项又要调用判断一些函数是否相等,这种算法导致的耦合度爆炸也难以避免.

自己 or 发现同学的bug的策略

本次作业没有在三测中发现任何bug.

在本地测试的时候,发现对于空表达式会愚蠢的只输出(),而自己只在顶层的表达式补了0.这一点很快得到了修正,判定输出是否为空被封装到了Expr内部,还巧妙地避免了再在main方法中判定是否为空,结构化效果更好了.

在互测中,读代码依旧没能发挥用处,所有的bug都是评测脚本跑出来的.一位同学的正则括号写错,导致错将一些输出判定成了WF,但归根揭底不是什么大错.

心得体会

  • 自己阅读代码的能力非常弱,急需加强

  • 测试找bug的能力需要提高.自己之前奉行的质量控制政策是克劳斯比模式,所谓"质量控制的核心是预防,而不是检验",争取一次将代码写对,但是往往轻视了后期的测试环节,这导致在互测环节的发挥不好.

  • 面向对象的思想需要进一步提高,这一点,需要阅读更优质的代码,不断向其靠拢.现阶段类之间的耦合非常爆炸.

  • 自己的代码长度相比大佬同学较长,这是因为自己想不出那么简单的算法.但是毕竟时间相对还算宽裕,即使代码复杂点,最终都能完成,只要做好质量控制,也未尝不是一种策略.

你可能感兴趣的:(BUAA_OO_2020_第一单元作业总结)