前三次的OO做业的内容总的来讲都是围绕着多项式求导,从最简单的x的幂函数的求导逐渐增长难度,最后完成含有三角函数和嵌套因子的多项式求导。可是在这三次的程序编写和debug中,我也出现了大大小小的问题,因此在此,我对于OO前三次做业的完成作一个总结,使本身对于存在的问题可以认识得更加清晰。java
题目要求正则表达式
简单多项式求导,多项式的每一项中只含常数和x的幂函数。编程
思路数组
在第一次做业中,多项式的构造比较简单,因此在这个时候为了更加直观、方便,我选择了正则表达式做为主要的工具对于多项式的每一项进行获取(这也为第三次做业遇到的困难埋下了伏笔),具体的正则表达式以下模块化
(第一项符号比较特殊,单独判断)函数
第一项的正则表达式:工具
Pattern p0 = Pattern.compile( "([+-]?[ \\t]*)([+-]?)(\\d*)([ \\t]*[*]?)" + "([ \\t]*x?)" + "((?:[ \\t]*\\^[ \\t]*[+-]?\\d+)?)"); Matcher m0 = p0.matcher(str);
其余各项的正则表达式:学习
Pattern p1 = Pattern.compile( "([+-][ \\t]*)([+-]?)(\\d*)([ \\t]*[*]?)" + "([ \\t]*x?)" + "((?:[ \\t]*\\^[ \\t]*[+-]?\\d+)?)"); Matcher m1 = p1.matcher(str);
(以上正则表达式中存在对于空格的判断问题,在bug分析部分将会具体分析)测试
在分离出每一项以后,开始进行常规的求导得到每一项求导后的系数和幂指数,最后将这些结果存入一个ArrayList中(同时进行合并同类项),最后进行输出。优化
优化
由于此次做业中只包括常数项和x的幂指数项,因此在优化上主要进行了常数项的合并以及幂指数相同的项系数的合并。
整体结构介绍
因为第一次做业须要完成的实现比较简单,而且在OO学习的开始面向对象的思想尚未创建起来,因此在第一次做业中我主要仍是按照计算过程建了四个类,分别进行每一项的提取、求导、计算长度,以及结果的输出。
类图
从类图中能够看出,程序主要仍是以面向过程的开发为主,而且没有更具体细化每个类的功能。
主要度量图
由度量图能够看出,在代码中的一些方法中,ev(G)和v(G)的值过大,说明这些方法的非结构化程度较高、独立路径过多,这也为代码以后的维护带来了困难。除此以外,代码中的类的WMC值广泛偏高,PolyDiff类的Ocavg值也偏高,这也代表着代码的复杂度太高,代码在设计上存在很多不合理的地方。
本身出现的bug
在第一次做业中,个人程序所有栽在了空格的识别上。
<space>
和\t
的空格判断出来;\\s*
.其实上述的错误都是一些因为理解或粗心而形成的错误,这也充分体现出了我在写代码时对于寻找本身的bug过于忽视,想固然地认为程序已经达到本身的预期效果。须要警醒!!!
他人的bug——以此为戒
因为第一次的做业比较简单,因此你们的bug都属于比较粗心的错误(好比容许指数中负号与数字间有空格),主要引起这些错误的缘由仍是由于对于指导书的理解不足。这也体现出了理解题意的重要性,而且在写程序前也应该先预计好可能引起bug的数据从而更好地避开这些致命的小bug。
题目要求
对于含有常数、x的幂函数、sin(x)的幂函数、cos(x)的幂函数的多项式进行求导。
思路
因为第二次做业的因子只有固定、较为规则的四种类型,因此我在最开始读入多项式时将全部的项分为了四个部分,并运用了一个数组存入每一个项分别的整个项的系数、x的指数、sin(x)的指数、cos(x)的指数(这也使第三次做业须要从新进行构造)。接着经过求导的公式,对于每一项进行求导,最终获得一个ArrayList并将其输出。
下面是正则表达式的格式(此时已经去掉了表达式中的空格)(因为排版问题,正则表达式的格式可能出现问题):
String teststr1 = "[+-](?:(?:[+-]?[+-]?\\d+)|(?: [+-]?" + "(?:(?:sin\\(x\\))|(?:cos\\(x\\))|x) (?:\\^[+-]?\\d+)?))"; String teststr2 = "\\*(?:(?:[+-]?\\d+)|" + "(?:(?:(?:sin\\(x\\))|(?:cos\\ (x\\))|x)(?:\\^[+-]?\\d+)?))"; String teststr = teststr1 + "(?:" + teststr2 + ")" + "*"; Pattern p = Pattern.compile(teststr); Matcher m = p.matcher(str);
优化
值得注意的是,此次做业中的sin(x)和cos(x)的格式都很是固定,因此还能够对于$sin(x)^2+cos(x)^2=1$进行化简。在此个人化简策略是在将每一项求导结果加入最终的list的时候就经过查找将符合$sin(x)^2+cos(x)^2=1$的输出项合并,所得新的项在此进行list的插入操做,运用一个递归将最终输出的内容尽量化为最简。
整体结构介绍
在本次做业中,我创建了五个类,分别进行输入、输出、提取项、提取因子、对于因子进行求导等操做。可是能够看出,在编程思想上此次做业仍是受到了不少面向过程的思想,而且程序的复用性极差。
类图
在本次做业中总共创建了5个类,可是每一个类的功能仍是不够具体,这也致使在下一次做业中没法对于这些类进行沿用。
主要度量图
由此次做业的度量图中能够看出方法PrintPart和InsertList的复杂度都太高,这也与其内部带有递归相关。同时对于类的复杂度,能够明显地看到DealWithPoly类的复杂度太高,这也是因为在DealWithPoly类中进行输出时运用过多判断语句,使代码过于冗长。
本身的bug
在第二次做业中总共出现了两个bug:
他人的bug
此次互测中我发现的他人的bug只要仍是格式判断的错误,其缘由仍是没有对于指导书中的格式要求考虑全面。
题目要求
此次做业的主要要求还是进行多项式求导,主要的求导部分还是常数、x的幂函数、三角函数的幂函数。可是不一样之处在于在此次做业中加入了嵌套因子以及表达式因子,表达式的读取方式需变为一种递归处理。
思路
刚开始拿到题目时,本身一直在思索应该怎么运用正则表达式来表示出表达式的形式从而判断表达式的格式是否正确,可是苦苦思索无果(java的正则表达式中没法进行递归,且运用正则表达式定会爆栈)。最终,经过理解指导书中推荐方法以及讨论区大佬们对题目的理解,我决定放弃掉在前两次做业中均使用到的长正则表达式,决定使用其余的方式来判断表达式格式是否正确,如判断括号、在局部运用小正则进行特殊格式判断。
判断格式是否错误的正则表达式举例:
Pattern p = Pattern.compile("\\*[\\+-]+(?:x|s|c)"); Matcher m = p.matcher(str); Pattern p1 = Pattern.compile("\\*[\\+-]{2,}\\d+"); Matcher m1 = p1.matcher(str); Pattern p2 = Pattern.compile("\\^[\\+-]{2,}\\d+"); Matcher m2 = p2.matcher(str); Pattern p3 = Pattern.compile("[\\+-]{4,}\\d+"); Matcher m3 = p3.matcher(str); Pattern p4 = Pattern.compile("[\\+-]{3,}(?:x|s|c)"); Matcher m4 = p4.matcher(str); Pattern p5 = Pattern.compile("sin\\([\\+-]{2,}\\d+"); Matcher m5 = p5.matcher(str); Pattern p6 = Pattern.compile("cos\\([\\+-]{2,}\\d+"); Matcher m6 = p6.matcher(str);
在初步判断表达式的格式后,我经过+
和-
将表达式分割为项,以后再经过*
将每一项分为符合指导书要求的因子。以后再求导操做中,我按照指导书中的因子类型,将须要求导的因子分为<u>常数型</u>、<u>x的幂函数型</u>、<u>多项式因子型</u>、<u>sin(factor)的幂函数型</u>、<u>cos(factor)的幂函数型</u>,并为其创建各自的类,其中因为sin和cos的括号内部能够做为一个因子,因此咱们能够再次调用因子求导的类,判断其中的因子是否知足格式要求并进行求导。
优化
在第三次做业中,因为我为了方便而在输出的list中没有将每项的系数和幂指数进行分开而是将每一项做为一个系数和字符串的结合体,因此这也使我在想要进行优化时带来了麻烦。可是考虑到本次做业除了常数项可以合并的同类项并非不少,其实在进行优化时咱们只考虑常数项的合并,并将能去掉的括号不进行输出,也可以取得不错的优化效果。
整体结构介绍
在本次做业中,面向对象的思想终于稍微出现。在做业代码中,总共有10个类,其中除了输入输出、获取因子和项以外,还创设了对于各类因子不一样的求导类。
可是,在本次做业中,我忘记使用继承及接口(说到底仍是没能理解继承和接口的精髓),致使不少类似的类中的类似代码屡次出现,而且没有对于类似的类规定统一的接口。
类图
分析类图,能够清晰的看出,在程序中有许多类似的类没能进行程序代码和接口的统一。
主要度量图
从方法的度量表中能够看出有关求导后结果的list建立的函数中的代码复杂度太高,方法难以进行模块化、难以进行测试和维护,方法的可复用性不高。这也是因为这些方法中的循环和判断语句使用过多形成的。除此以外,PickUpTerm类和PickUpFactor类也过于复杂,对于这些类,复杂度太高的主要缘由主要仍是运用了面向过程的方法,没有能很好的将它们的功能进行分割。
本身的bug
在此次做业中,因为在写代码时没有可以保持一个比较清醒的态度,代码也没有很好的模块化,致使有些前面考虑到的状况在后面写相关的代码时忘记了。体如今代码上即是,在进行表达式因子的读取时,没有考虑前面符号(其实在前面的代码中已经对于符号进行了判断)。因为这一行的错误致使强测和互测被刀到肉疼!
这也提醒我在写代码时必定先进行完备的考虑后再进行代码编写,而且在测试时也不能仅仅依靠现有的测试数据,要尽量寻找可能出现的错误,并将其进行组合。
他人的bug
因为此次不容许wf数据的测试,让一些同窗逃过了一劫,可是从我找到的错误来看,你们主要仍是格式的判断的失误(像我这样因为求导过程的错误实在很少/(ㄒoㄒ)/~~)。主要的错误有:在三角函数中没能正确判断因子格式、在乘法中错误判断格式(主要都出如今常数项的符号判断上)。
就着第三次做业我来谈一谈本身在寻找bug时的策略:
虽然目前为止,我经过现有的方法找到了很多bug,可是相比同组大佬仍是相差甚远,因此我也须要在从此的debug之路上更进一步,能够对于程序产生随机数据上进行探索~~(虽然我以为经过思考构造数据可能找到bug的效率更高)~~。
在本次实验中,因为咱们的求导操做经过因子的类型区分为不一样的过程,可是总的来讲都是在进行求导这一操做,因此咱们能够选择使用工厂模式。
**工厂模式:**工厂模式是一种实例化对象模式,是用工厂方法代替new操做的一种模式。具体操做主要为创建一个工厂类,对实现了同一接口的一些类进行实例的建立。
重构具体方法:
回顾完成这三次做业的过程,能够明显感觉到本身在这一阶段的进步----从一个java小白到如今能够较为熟练地运用java的基本语法,而且对于面向对象编程有了初步的了解和实践。可是不能否认,本身目前仍是一个小菜鸡,在程序的构造和debug的能力上有着很大的欠缺,编程的思路也常常很不清晰,对java的了解也不深刻。
在接下来的学习中,我也须要有所针对,要不只能学其所用,更能用其所学。