三次作业分析
第一次作业:
第一次作业式只包含幂函数的多项式求导,总的来说比较容易算出正确的结果,但由于是第一次接触面向对象式编程,对于面向对象的思想还完全不理解,所以我是以第一次上机的矩阵那个程序的结构为模板进行的编程,并且基本采取的是面向过程的思路。我建立了一个多项式的类,以字符串的形式来存储输出的表达式,在判断合法性之后再对其用加号进行拆分,然后进行求导运算,最后再输出之前进行化简。(然而,其实就是把面向过程中处理的步骤扔到了一个类里面2333)
类图如下:
度量分析如下:
在第一次作业中,我认为最重要的有两个方面。一是使用正则表达式来对输入进行判断。刚开始,我采用的是一整个大正则来对整个输入串进行匹配,然而发现只需要输入500个+x就可以轻松爆掉,在偷偷的看鱼塘里的大佬们讨论的过程中发现是由于一个大正则匹配进去会爆掉栈,于是使用了逐项进行匹配的 方法。最开始,我打算采用find方法,然而对于错误的输入,如果其中有正确的部分就会被认为是合法的,为了解决这类问题,我自行百度了find的start和end方法,然后发现使用find还不如用lookingat,于是我就使用了lookingat方法,每次匹配到一个,就将其删掉,直到匹配完为止,成功不会被500个+x爆掉。二是优化,我采取的优化方式是每一项使用系数和指数的二元组形式记录下来,然后通过遍历,对指数相同的进行合并。
优点:正确性良好,第一次作业在强测中没有出现错误,在互测中也没有出现错误(这都多亏了面向过程(大雾))。
缺点:(1)完全采取的面向过程的思维。(2)所有的处理都集中在Polynomial这一个类里面,非常容易出bug并且极度不好维护找bug(我就是因为有一个地方少打了一个*导致改了整整2天,濒临绝望)(3)很少写注释(自己再回顾的时候都要看不懂了,在此向全体狼人种表示崇高的敬意。
第二次作业:
第二次作业在第一次作业的基础上增加了正弦以及余弦函数的幂,并且支持乘法的求导运算,相对于第一次作业来说,变化不是很大。这个时候,我对于类的理解大致停留在:类似C语言的结构体,把一些有关的量存在一起并且把对这个结构体的函数也写在里面(捂脸),所以我基本还是采用了第一次作业的方法:先正则判断,建立表达式类,然后按照加号分割,然后对每一项求导(乘法的求导使用遍历来实现),我在这个类中写了一堆方法(完全当成了C语言中的函数2333)来支持求导运算,再加上乘法求导的遍历等,这一个类被我写了整整350多行。在优化过程中,我发现每一项都可以写成a*x^b*sin(x)^c*cos(x)^d,于是类比第一次作业,建立了四元组来进行化简。同时,通过爬鱼塘,发现了大佬们讨论的三角优化,然后菜鸡的我并没有想到很好的优化方式,只处理了最最最最最简单的sin(x)^2+cos(x)^2=1(约等于没有优化)。
类图如下:
(基本和第一次作业一模一样2333)
度量分析如下:
第二次作业中,我认为老师是想让我们对不同的类型建立类来处理,来开始体验面向对象式的编程(然而我还是采取了面向过程式,不过在之后我也尝试着使用面向对象式的思维去写)。并且,由于乘法求导需要遍历,导致我写的求导方法超过了60行的checkstyle要求,于是我还把重复的代码写成了一个新的方法来减少行数,(后来发现,提前把能够复用的代码写成方法才是正确的做法)。然而最最最最最让我意料不到的是,我居然连互测都没进去,最后发现没进去的原因是对合法性进行判断的时候,在乘法 循环的时候删掉了匹配过的字符忘记更新matcher类23333,然而这只会在三个以上因子乘在一起的项连续出现的时候才会出错,我自己居然当时也没测出来(没进去互测的第二天我就试出来了(流泪)),不过我搜集了我认识的同学hack别人的点,发现他们的哪些问题我都没有,我貌似就只有这一个少写一行的问题......
优点:排除那个bug之外的正确性(依旧是面向过程的遗产),增加了一些注释,使得代码具有了一些可读性
缺点:(1)还是主要是面向过程的思维(2)代码过于冗长,缺少可读性,不易改bug(3)测试不充分,受第一次的影响,跟测评姬一样执着于寻找wf的形式,二却忽视了把对的判成wf(4)一点都不面向对象
第三次作业:第三次作业是包含嵌套表达式的多项式求导,相比前两次作业来说有一个较大的难度提升。由于我前两次都是采用先判断合法性,再通过+号分割来分离出项,所以这次我也还是采取大致的思路。(虽然在鱼塘里听大佬们谈论递归下降和表达式树,经过我自己的百度我还是决定采取我自己熟悉的想法,太菜了呜呜呜)由于此次作业有着表达式的嵌套,而如果按照之前的办法,表达式因子会被我拆分成不同的项,所以我先手动寻找了括号的位置,然后将表达式项的+ *号都换成了用不到的符号,在计算求导时再换回来。本次作业由于包含表达式因子,所以一个项的长度无法估计,我之前采取的逐项匹配合法性的思路也不再能对每一个项写出正则表达式,于是我在分割前先对整个式子进行了空格,特殊字符以及符号的合法性判断(因为我要根据符号去分项),然后在构建每一个因子类的时候再对每一个因子进行合法的判断。对于求导运算,我对每一种因子都建立了类,并且建立了一个接口去集成求导方法(虽然没多大用,但是起码看上去更面向对象了是不是,菜鸡如是安慰自己)。
类图如下:
度量分析如下:
可以发现,由于我是递归构造的,表达式类 和因子类都被标红,说明了我的构造结构还不够好(果然硬刚不会有好结果2333)。
优点分析:思路简单易懂(或许这可以算作是一个优点)
缺点分析:(1)不够面向对象(2)思路易懂但是代码可读性差(3)细节问题,再某些地方上少打了某些字符导致debug十分痛苦(4)对于表达式因子以及因子的省略等规则理解的不够透彻(由于我在分割的时候就对符号进行了一些操作,导致后面的一些本来正确的形式被我替换成了错误的,曾导致了大面积的WF)(5)没有优化(为了避免bug我甚至还让结果变得更长了)
BUG分析:
第一次作业:
我方servant:强测与互测均没有发现bug(虽然有一些优化没有进行完全)
敌方servant:主要存在的是WF的问题,无法判断WF以及误判
第二次作业:
我方servant:由于判断合法性的时候少写了一行字,互测都没进去0.0,其他同学互测的数据我改正了之后基本没有问题(虽然还是优化不完全)
敌方servant:我方提前阵亡,并未遭遇地方servant
第三次作业:
我方servant:对于表达式因子的误解导致把某些合法的判断成了不合法或者输出的格式不符合表达式因子,被aoe各种误伤
地方servant:本次地方servant在鲁棒性方面出现了大量漏洞,然而被圣杯保护,无法针对其弱点发动宝具。除此之外,地方servant在对于嵌套的表达式因子的处理上也出现了破绽。
寻找敌方master破绽的策略:
首先要结合自己课下所遇到的问题去重点阅读某些部分的代码,以我方servant的弱点去试探
其次,再采取
(1)鲁棒性测试:针对f 以及多重符号等,对敌方servant施以虚数魔术
(2)压力测试:对于爆栈等问题的测试,对敌方servant进行大面积魔术进行轰炸
(3)边界测试:针对一些经常会忽略的边界情况进行测试,对敌方servant采取鲜为人知的术式进行攻击
(4)自动生成测试样例:使用python等编辑脚本,生成大量数据来测试,对敌方servant使用宝具——巴比伦之门
(5)找大佬同学要数据:寻找强大的master结盟以保证存活
等方式进行圣杯战争
单元总结:
第一单元的三次作业主要是让我们从之前面向过程的思维逐步的向面向对象思维转变。一些之前从未关注的点(比如鲁棒性)等渐渐的都需要我们考虑,思维的完备性也得到了锻炼,这对于我们之后走向社会有着极其重大的意义。同时,互测的机制也让我们提前接触到了职场的工作模式,同时欣赏学习别人的代码也是一种非常有效的学习方法。总的来说,第一单元的体验还不算差(虽然有一次互测都没进)。
一点小小的建议:
有的同学可能强测得分低只是由于有一个或者很少的bug没有de出来,所以我提议,在强测后,互测前,给那些强测得分低的同学一个机会。
其次,七八个人一个room可能有一点点多,我基本每次都看不完所有人的代码,一般都是只看前面的3-4个,后面的基本随缘去炸