OO第一单元总结~~
经历了三次oo作业的洗礼,我感觉我的IDEA已经是一个成熟的IDE了,应该学会…(划掉)。总之学习面向对象编程是一个思路慢慢转变的过程,最开始的时候我觉得其本质就是封装,类的函数在一起,属性在一起,后来又学习了一些之后,认识到了其另外两个特性,继承与多态,学习了抽象基类与接口的使用。
一、三次作业的基本思路
第一次作业只是简单的多项式幂函数相加,第一次听到这个题目的时候我心想:这不是很简单的嘛??因为其实最初的想法就是,像结构体一样写一个Item类,属性有两个:coe和index。
紧接着我看到了指导书:需要判WRONG FORMAT,我陷入了沉思…我果然还是图样图森破。实际上对于正则表达式的应用很多人在写pre作业的时候就已经学到了,但是我那时候还坚持使用C语言的字符串处理写完了…所以我就开始了我的正则学习之路。
第一天下午,我的正则长成这个样子:20+行的大正则匹配一整个式子。但是还没完工我就已经感受到了深深的不对,这恐怕绝对不是正解。然后在我的思索之下有了正则2.0版本,大致写法是如果检测到‘*’再去检测后面的x部分,就是用一堆if else去检测匹配,然而在一晚上的debug中我最终放弃了这个写法。
最终我想到了一个判WF的流程:因为所有关于空格的错误只有这三种:
++ 500
123 234
x^+ 100
所以我写了以下三个格式去匹配:匹配到就算错
这样下来,最令人头疼的空字符问题就解决了,接下来我删除了所有的空字符,相当于做了一个预处理,但是节省了一些麻烦。之后对于其中的五种格式进行匹配:(a b代表数字)
a a*x a*x^b x x^b
毕竟错误千万条,正确只有一条,第一次的优化也很简单,只需要写一个hashmap就可以了。
第二次作业可以说我是几乎90%的沿用了第一次作业的(不可拓展的菜鸡)架构 ,所以这个周末我过的十分的安逸,其实预想到之后可嵌套形式的大佬们都已经开始写大架构了,而我丝毫没有察觉到危险的来临…
因为这一次作业仅仅是加入了三角函数而已,而且三角函数里面也只能是x,所以求导依旧不是什么难点。Item的成员变成了如下四个,求导方法加入了三角函数,输出也改了改。然后对于识别项的时候先用加号分割再用乘号分割,继续用matcher一个一个匹配,毕竟这次总共的形式也只有7种:
x^a cos(x)^a sin(x)^a cos(x) sin(x) x a
最后优化的部分是写了一个Key类,重写了一下equals方法。
第一次看到第三次作业的指导书的时候,我停止了思考,这时候才开始考虑真正的求导,于是请教别人知道了树结构,于是我的整个架构都有了大改,开始思索怎么建类,怎么好好使用抽象类与接口,这是学习了3周的oo之后第一次去思索这个问题,怎么递归的去建树。这次的有多个括号也可以说是一个问题,所以我又做了一些预处理例如括号匹配…
求导的话当然也是递归的求导了,由于我这次完全没有考虑优化所以就是字符串硬莽,直接返回字符串,套很多括号的那种,所以在之后的研讨课上听了大佬的一些优化的发言,我也开始考虑是不是可以尝试写一下优化。
二、基于度量分析我的程序架构
我的前三次作业的类图如下:
第一次
第二次:
第三次:
三次的度量图:
第一次:
第二次:
第三次:
从结构与度量上来看第二次作业可能是做的最不好的一次了,第三次相较于前两次都会好一些。可能是因为第二次单纯的沿用第一次的架构,只是堆砌似的增加功能。
三、欢乐圣杯战
oo的互测可以说是变成了一件有趣的事情。
这几次作业我被发现的bug有如下几类:
第一类是因为优化而产生的bug
- 比如有一个样例我的输出是:
-820510*x^-510*x^-400*x^9980*x^-570*x^-91
乍一看好像是一堆连乘,而其实是
-82051 0*x^-51 0*x^-40 0*x^998 0*x^-57 0*x^-91
因为漏掉了这个不该输出,而且忘记了输出加号而导致的问题。
- 还有就是优化逻辑导致的错误,比如在hack别人的过程中发现有的人在结果为0的时候就完全没有输出了,这代表本来想优化却漏考虑了这些情况。
- 优化的时候直接把所有的-1替换为‘-’,把所有的+1替换为‘+’,这显然就漏考虑了很多的情况,比如x^-1等等
第二类是程序逻辑所导致的错误:
- 第三次作业中,我的sin(-100)输出了WF,原因是我在建树的时候把-100变成了-1*100并返回了一个乘结点,由于sin内部不能嵌套乘结点,因此在求导的时候就出现了错误。
第三类是因为没有考虑可能出现的情况导致程序出现crash的错误:
- 通过异常的追踪可以看到发生异常的地方常常是字符串下标越界或者是matcher根本没有找到却有了下一步的取得group,这类的情况往往是因为数据本来就是WF而引起的,所以解决的方法就是加以判断,否则输出WF,不过听到很多前辈们说可以直接写一个大的try语句套住整个main函数,直接catch所有的异常,不过我觉得这样做最好是基于自己程序对于正确数据的判断能够完全的没问题不会报异常,否则的话有可能出现正确数据输出WF的情况。
关于测试:
我觉得测试的好的境界就是程序没有写完的时候就根据自己将要写的架构去构造很多的测试样例,之后再放入单元测试中。
在几次的互测中,我开始会去尝试看同学们的代码,尝试找到逻辑中的错误,但是后来发现很多人的代码可读性并不是很好而且要一口气看完其他好几个人的代码并且思考逻辑漏洞,实在是有一点不太现实。所以后来我就开始盲狙,把各种我自己觉得容易错的样例扔进去测试,一般总是会碰到不少。但是在有时候进了高端互测屋的时候这个方法就不是很奏效了,因为这些代码并不怎么会犯低级错误,所以就我目前的水平我只能找大佬同学借来了对拍器去做测试…所以说自动化测试是一种很重要的方法,我希望之后能够学习写这样的代码,写出自己的对拍器。
四、第三次作业可以做的改进
首先第三次作业我并没有做什么优化,很普通的数据都会输出好几行,所以我的想法一个是在树的结构上直接做优化,根据求导的结果返回更加简化的求导结果,例如0*x^a,就直接返回零,尽量减少括号的嵌套,我感觉这一部分可以通过字符串处理的方法来解决。
我感觉我的类的结构也可以进一步的实验优化,因为其实sin和cos类的求导是很相似的,所以可以把他们设为一个共有父类,其余的x的指数共同设为一个父类,+与*共同设为一个父类,然后都实现求导接口,这样就能够再次简化代码量。