BUAA OO 2021 第一单元总结

BUAA OO 第一单元博客总结

写在前面

面向对象(OOP)是一种思维方法,与面向过程编程不同,面向对象的前提是你得有一个对象 不是npy那种对象 然后与对象进行互动,因此需要重视对于对象行为的抽象,封装与重用等。因此在刚刚接触面向对象思想的时候,需要扭转面向过程的思维,拒绝一main到底,而是将“高内聚,低耦合”进行到底!

第一次作业总结

  • 思路

    第一次作业较为简单,限制也较少,所以我使用了一些难以拓展却能让思路更清晰的方法。首先将输入的字符串进行预处理,包括替换正负号,去除空白字符等,然后根据加减号对各个项进行拆分,进而再对各个项中的每个因子进行拆分,从而建立了一个伪递归下降算法。

  • 类图分析

image
简单的类图,不需要太多解释,只需要知道本次的结构为两层的嵌套,即多项式可以化为多个单项式的和。

  • 代码度量

    方法复杂度分析:

    image
    经分析可知两个类中重写的toString()方法基本复杂度较高,原因是其中有很多化简算法;Term.toString()Term.Term() 一路飙红,是由于其优化还不到位,也可能是由于优化,重复造轮子,使圈复杂度很高。
    类复杂度分析:

    image
    OCavg代表类的方法的平均循环复杂度。二者较高,是由于使用了while(matcher.find()) 来识别每一部分的字符串。

  • Bug分析

    • 个人

      在中测与强测中均通过正确性测试,也未被hack。

    • 互测

      第一次进行互测,我就采取了直接看别人代码的方法,虽然很笨很累,但有一定效果。本次成功hack的样例点是存在三个连续正负号的样例。由于第一次作业对于表达式的理解较差,对于连续多个正负号的理解不深,导致对于这种样例没有处理。
      构造的数据包括:

      • 边界数据:0,1,-1
      • 超大数,如:98765432123456789
      • 正负号奇特数:--+1*x+-+3*x**4
      • 奇怪格式的数:000000151*x**3-0*x
  • 性能分析

    第一次作业的性能优化难度不高,在合并各个因子的常数项和幂指数后,可优化的点为

    1、将x**2输出为x*x,可以减少一个字符的输出

    2、将每次输出的第一项调整为正的,可以减少一个字符的输出

  • 反思

    第一次作业难度不高,在刚开始时对于连乘项的处理有点棘手,但将其拆分成为各个小表达式后难度陡然降低。这也为后期的递归下降方法埋下了伏笔。

    但对于细枝末节的优化并没有做到位(因为比较懒),但其实只要对于整体的表达式做同指数的项的合并,就可以得到很高的性能分了。

    本次作业课后的实验课上学到的重写,化简思路等,给本次作业带来了极大的帮助。

第二次作业总结

  • 思路

    第二次作业增加了括号嵌套和三角函数因子,难度提升幅度很大,而递归下降算法的引入,则可以很好的解决嵌套问题,并且能够很清晰的分析出各个表达式、项、因子的层次关系。

    本次作业使用递归下降算法,输入的整个字符串为表达式,表达式分为各个项(即表达式是多个项相加减),项分为各个因子(即项是多个因子相乘),而因子分为常数因子、多项式因子、正弦因子、余弦因子、表达式因子。

    该算法的难点以及核心,就是表达式因子的引入,正确理解并合理表示出表达式因子在整个结构中的地位,便可以很好的完成本次作业。

  • 类图分析

image
本次作业类图分析稍显复杂,但总体而言还是十分清晰的。最大的类为Expression(表达式)类,在这个类中包含一个Term的列表,各个Term之间的关系是相加的;Term(项)类中包含一个Factor的列表,各个Factor之间的关系是相乘的;Factor分为五个部分:Power类,Sin类,Cos类,Constant类以及递归下降算法的核心:ExpressionFactor类,即表达式因子类。

  • 代码度量

    • 方法复杂度分析

      image
      本次的方法复杂度图中仍然是各个 toString() 方法一路飘红,可能是由于其化简算法较为复杂,并且每个toSring() 中必定调用其他类中的toSring(),使得耦合程度较高。

    • 类复杂度分析

      image
      OCavg代表类的方法的平均循环复杂度。
      WMC代表类的总循环复杂度。
      本次类复杂度优化很好,仅仅是Term类的循环复杂度稍高。

  • Bug分析

    • 个人

      第二次作业出现了较多的bug:

      1、 由于对递归下降算法分析不够透彻,对 表达式—项—因子的结构分析不太明确,导致在划分层次时对于表达式因子的正负号忘记提取。

      2、 在重写toString算法时,由于要对输出结果进行判断和简单化简,调用多次其子元素下的toString方法,而这样的结构层次在执行 ifelse if 函数时,会导致多次调用,多次判断,使得toString函数性能急剧下降,在嵌套大约7层括号时就会超时。此bug困扰了我很长时间,但最终通过性能分析工具定位到影响进程的主要因素后,很容易就可以解决。

    • 互测

      由于本次作业强测得分不高,导致分组较差,组内的bug异常丰富,可以总结为以下几点:

      1、 括号嵌套问题,如 ((0))

      2、 连续三个正负号的问题,如 --+1*x+-+3*x**4

      3、 负数乘除问题,如 (x*-1)*((0))

      由于我没有搭自动评测机,所有hack成功的样例都是靠研究代码然后构造边界数据所实现的。效率很低,但由于组内bug实在是太多了,成功率还是很高的。

  • 性能分析

    本次作业,由于括号嵌套与三角函数的加入,使得表达式化简十分困难,因此我只进行了简单的化简工作:将每个项内的常数因子相乘合并,三角函数项同类别的相乘合并,幂函数项同类别的相乘合并,而对于括号的拆分合并,不同名三角函数的合并并没有实现。

  • 反思

    第二次作业与第一次作业相比,难度陡然增加。究其原因,是因为第一次作业并没有将整个表达式的层次理解清楚,而走了很多弯路。

    递归下降算法是本次作业的核心与重点,更是难点,前期的理解十分重要。在正式开始编程之前,可以画出大致的类图,标清各个类的关系,可以显著提高正确性,也有助于提高代码的可拓展性,并更容易被理解。

    在写完程序通过弱测后,也没有进行后续的debug工作,导致很多明显的bug并没有修复,这就使得强测分数极低,这应该是之后的作业要注意的问题。

    对于 toString() 方法的重写,应当时刻注意 null 与空字符串的区别,最好二选其一,防止输出混乱,在判断化简时容易出现难以察觉的bug。

第三次作业总结

  • 思路

    本次作业是第二次作业的拓展,整体思路仍为递归下降算法,而增加了三角函数的嵌套因子以及不合法输入数据的检测。这时,对于形式化表述的深入研究就显得尤为重要。

    经分析后,嵌套因子只需将第二次作业中的三角函数类中增添一个表达式因子的属性,并修改求导方法;不合法数据的检测则需要修改匹配时的正则表达式,在正则表达式中依照形式化表述增加空白项,并要求在匹配过程中不断判断matcher.start() 是否与上一次的 matcher.end() 相等,以此来判断输入正确性。

  • 类图分析

image
​ 第三次作业的类图分析与第二次可以说极为类似,唯一的不同就是在Sin类和Cos类中增加了factor属性。

  • 代码度量

    • 方法复杂度分析

      image
      不出所料,仍然是toString() 方法一路飘红,原因应该与上一次类似,毕竟第二和第三次作业的思路几乎相同。

    • 类复杂度分析

      image
      还是不出所料,仍然仅仅是Term类的循环复杂度稍高。

  • Bug分析

    • 个人

      本次作业经过所有中测强测的正确性测试,但在互测中被hack出一个bug,即:

      对于表达式因子的 toString() 方法,在返回字符串时,由于对性能的优化,在输出为 0 时 ,我采取的策略是返回空字符串。但当三角函数的表达式因子返回值为空串时,未进行特殊化处理,使得括号内部没有任何字符,导致格式错误。

    • 互测

      本次作业代码量稍大,使得对于代码的研读产生一定的困难,因此在互测时没有很多有效hack。经过了本次之后,愈发意识到了自动评测机的重要性,奈何本人水平有限,难以抽出精力搭建自动评测机,只好构建边缘数据较多的测试样例,扔几颗深水炸弹,炸到血赚,炸不到也不亏。

  • 反思

    本次作业难度梯度与上次相比下降了很多,如果第二次作业完成较好的话,第三次的代码只需进行小幅度的修改便可以完成,其感觉像是上学期计组从p4-p5的难度与从p5-p6的难度。

    遇到最大的问题就是不合法输入数据的检测,而判断思路也千人千法,找到一个最合适自己的,理解最透彻的,是很重要的。

重构经历总结

说实话,三次作业我经历了三次重构,因为每一次都会觉得上次的代码就是一坨*,而每一次的重构都会使得我的代码更加优美整洁。

最大的一次修改发生在第一次作业和第二次作业之间,因为第一次作业在目前看来还保留着大部分面向过程编程的身影,十分不利于后期的拓展,第二次与第三次的差别就比较小了。

而第二次与第三次的最大差别是对于三角函数中嵌套因子的处理,这里进行了较大规模的重构,但整体类图并没有发生太大改变。
第一次作业过于简单,省略其分析。

  • 第二次作业详细类间关系图
    image
  • 第三次作业详细类间关系图
    image
    我们可以从图中明显看出 SinCos 类在这两次作业中的差别,复杂度明显提升,与其他类的耦合程度提高。

心得体会

经过了面向对象课程第一单元的学习与摧残,我终于开始了由面向过程思维向面向对象思维的转变,但这只是我迈出的第一步,对于问题的分析,首先要想的是如何将其抽象为对象,这是面向对象编程思维应该达到的境界。

posted @ 2021-03-28 14:55  是小鸿不是小红  阅读(81)  评论(1编辑  收藏  举报