第一单元的主题是表达式求导,第一次做业是只带有常数和幂函数的求导,第二次做业加入了正余弦函数,第三次做业又加入了表达式嵌套,难度逐渐提高。整体来讲前两次做业还易于应对,而第三次做业作得相对有些艰难。并且这其中还有不少巧合,第二次做业延时到了周三上午,而我在周二晚睡觉前通过本地测试又找到一处致命BUG,一直改到一点才交上;第三次做业一开始中测的最后一个点一直没有经过,周二找了一天原本已经放弃,后来得知做业又延时到周三中午,成功在周三上午找到了那处BUG,并正好用完十次无偿提交次数,终于过了所有中弱测。若不是这两次延时,第一单元的做业恐怕已经崩掉了。正则表达式
(1)类图与复杂度:数组
(2)程序分析函数
仅用了两个类。主类Derivation仅包括主函数入口。类Exp中包含一个private属性str[],它是用来储存项的字符串数组;四个方法,其中Exp()是构造方法,同时进行格式合法性判断和拆项,getLength()用来得到str的元素个数,diff()用于处理每一项、求导并将结果中的同类项合并,pri()用于输出求导结果。性能
能够看出diff()的方法规模较大,由于这个方法同时具备按状况处理项、求导、合并同类项三个功能,能够将这三个功能拆成两个或三个方法来写。Derivation类约有35行代码,Exp类约有115行代码,总共约150行代码。学习
(3)优缺点分析测试
第一次做业个人代码整体仍是比较面向过程,基本上按照C语言的模式去写的,面向对象的思想不够深入。spa
(1)类图与复杂度debug
(2)程序分析设计
第二次做业的总体思路是处理表达式后,分别得到每一项的系数、x的指数、sin(x)的指数、cos(x)的指数四个ArrayList,求导时则是系数乘以某一个因子求导的结果再乘以其余因子。对象
总共有Derivation二、Exp、Diffx、Diffcos、Diffsin五个类,其中Derivation2包含主函数的入口,Exp用于判断表达式合法性、处理表达式并进行求导结果的输出,Diffsin、Diffcos、Diffx分别是对三类函数的求导处理。
能够看出,Diffx、Diffsin、Diffcos三个类中的pri函数(对求导结果的输出函数)代码复杂度较大,由于其中为了简化输出结果,用了不少if-else语句判断指数为0、一、2等以及指数为负等状况。还有Exp类中的getList()方法(用于得到各个因子的指数)也用了不少if-else语句判断三种因子是否省略指数等状况。
另外,Exp中的private属性过多,由于判断表达式合法性的方法超过了60行,我就将三种因子及常数因子的正则表达式写到了类的private属性里。
(3)优缺点分析
写第二次做业时其实并无彻底理清思路,不少方法构造的比较复杂,甚至有为了缩减方法行数将方法内的变量移到类的属性中的草率决定。
(1)类图与复杂度
(2)程序分析
第三次做业的总体思路是先将输入的表达式进行递归判断是否合法,而后进行链式求导,其中存在对各类因子求导、对项求导与对表达式求导的相互调用。
共九个类,Main包含主函数入口,Exp用于递归判断表达式合法性,Factor做为父类,Da、Dx、Dsin、Dcos、Dterm、Dexp都是Factor的子类,分别用于对常数、幂函数、正弦函数、余弦函数、项、表达式求导。
能够看出方法复杂度最高的是每一个子类中的diff()方法(求导函数)以及Exp中的exchange()方法(递归去括号)和judge()方法(判断合法性),由于其中都用了不少if-else语句判断各类状况。
(3)优缺点分析
我的认为第三次做业仍是存在一些优势的,好比我独立完成了递归判断合法性代码的编写。缺点是没有对结果进行化简,个人程序是变求导边直接打印结果的,其中只有一些简单的指数为0、一、2等状况的简化,其它的都是直接输出,还存在不少无心义的括号。
第一次做业强测中有一处BUG,互测没有被找出BUG。
第一次做业原本也比较简单,我也作告终果的简化,包括首项为负时正项提早的化简也作了,本觉得应该一百分,结果强测有两个点没过。
这两个点都源于同一处BUG,我在求导以后合并同类项时,忽略了合并后系数为零的状况,也就是说我将系数为零的项也输出了,但本应该只是扣性能分,结果应该是正确的,但我在链接每一项时只判断了系数大于0时添上+号,由于系数为负时输出的BigInteger会自带符号,因此我对于系数为0项的输出前没有加减号,也就产生了相似于x^20*x的不合法结果。这个BUG本质上仍是粗心了,只考虑了输入时将系数为0的项忽略,而没将合并后系数为0的项排除。
第二次做业强测中有一处BUG,互测没有被找出BUG。
个人求导思路是对每一项的每个因子分别求导,并将某个因子求导结果乘以其它因子,而我判断了若是某个因子求导结果的指数为0,将其忽略,但我在输出时却无条件地在求导因子外的其余因子前输出一个*号,也就产生了相似于+*x的不合法结果。
还有一点值得一提的是我在周二极限de到凌晨一点的BUG,那就是我在对输入的表达式进行同类项合并时,因为我用了四个ArrayList分别储存每一项的系数、幂函数指数、正弦函数指数与余弦函数指数,我在判断同类项时写成了只要在三个存指数的ArrayList中找到了与当前项各个指数相同的元素,就将系数相加而不添加新的指数元素。但这样假如输入一个x+sin(x)+cos(x)+x*sin(x)*cos(x)的表达式时,就会将第四项判断为以前出现过同类项,而将表达式化简成2*x+sin(x)+cos(x)。因此在判断同类项时,应该判断在ArrayList中是否有当前项的各个指数全都对应存在的元素,若是有才是真正的同类项。
第三次做业强测与互测中都没有被找出BUG。
这里分享我当时中测最后一个点死活de不出来的BUG。我在递归进行合法性判断时,先判断括号中的内容是否合法,若合法将括号及其中的内容直接替换为字母x或字母e(换为e即表明当前括号及其中的内容是一个表达式因子)。我在替换时用到了replace函数,可是replace会一次性替换全部相同子串,例如输入的表达式是(x)+sin(x),我就会在第一次替换时直接将原式替换为e+sine,即认为正弦函数括号中为一个裸的表达式,从而判断为WF。因此应该用replaceFirst函数仅替换刚找到的子串。这里还须要注意的一点是,replace函数替换的是子串,而replaceFirst函数替换的是第一处符合正则的子串,也就是说,在找到一个括号子串(设为字符串s),在用replaceFirst函数式应先将s中的+、(、)等字符前加上转移符,以将s转换成正则表达式再使用replaceFirst函数。
因为本人很菜,前两次互测时都是先手动输入一些易错数据以及本身写代码时曾经出现BUG的数据来判断输出的正确性,而后就是肉眼debug了,即阅读被测代码的设计结构构造相应数据。这样的策略虽然效率不是很高,不过也能找到明显的BUG。在第三次互测时作了一次伸手党,从隔壁大佬那里整来了一个对拍器,找BUG的效率的确高不少。
每次做业中都有不少方法有不少if-else语句,其实有的能够拆分红多个方法去写。
第三次做业写的时候对继承的用法还不够熟悉,程序的实现对继承的依赖并不大,也并无使用接口,存在重构的必要。
另外但愿能够经过学习本身写出对拍器。