BUAA-OO-2019 第一单元总结

第一次做业

第一次做业须要完成的任务为简单多项式导函数的求解。html

思路

由于仅仅是简单多项式的求导,因此求导自己没有什么可说的,直接套用幂函数的求导公式就好了,主要的精力是花在了正则表达式上。这里推荐两个网站:git

<a href="https://github.com/ziishaned/learn-regex" target="_blank">https://github.com/ziishaned/learn-regex</a>github

<a href="https://regex101.com" target="_blank">https://regex101.com</a>正则表达式

前者能够用来学习正则表达式的语法,后者则提供实时的正则表达式匹配,方便进行调试和验证。尤为是后者,能大幅提升编写正则表达式的效率和正确性,省去了每次都得在IDEA里运行一遍的麻烦。编程

用正则时有一点须要注意,那就是大正则是不可取的。我一开始就是企图用一大串正则匹配整个输入,结果当时是爆栈了。合理的作法应当是每次只find()一个Term,以及经过Term与Term之间的链接关系判断合法性。数据结构

在数据结构方面,我一开始用的是ArrayList,但在后来优化的时候发现合并不是常麻烦,因而果断放弃,学习并采用了HashMap。经过将指数做为Key,能够快速地找到指数相同的项,以实现合并同类项的目的。多线程

程序结构分析

因为刚接触Java语言,也不太懂什么是面向对象思想,因此程序总体来讲仍是比较C type的。也多是因为本次做业比较简单,因此也并无用到继承、接口之类的东西。从类图中能够看到,一共也就两个类,其中PolyDiff完成主函数、预处理、合法性判断等任务,而Term则实例化出多项式中的每一项,保存系数和指数,并提供diff()求导方法。架构

度量分析

能够看出个别方法复杂度太高,这很不OO。下次应当多注意抽象以及代码重用,避免一个方法或者类的过于臃肿。函数

关于BUG

公测未被查出bug。在互测阶段,我第一个发现的bug是本身的bug,其缘由在于,我判断合法性的作法是将匹配了的每一项用"#"替换掉,最后看除了" "、" \t"、"#"以外是否没有其余的字符。然而,我忽略了输入一开始就含有"#"的可能,但这种可能性很是小,以致于我认为只要不仔细阅读个人代码,基本上是不可能歪打正着的。遗憾的是,最终仍是有一位十分认真的同窗,(我猜)在一行一行通读了个人整个代码后,竟然还真的发现了这个bug……我是服气的,真的太强了orz学习

至于我查别人的bug,都是用的我写做业时自测发现了bug的样例,由于我相信这样的样例会具备必定的杀伤力和普适性。事实证实,个人确能用这种方法测出不少别人的bug,因而也就没有再结合他人的代码设计结构来设计更多的测试样例。

第二次做业

第二次做业须要完成的任务为包含简单幂函数和简单正余弦函数的导函数的求解。

思路

第二次做业新引入了正余弦函数,乍看复杂了许多,但其实仍是能够用套公式的办法死作,然而代价就是毫无扩展性可言。

对于每一项及其导函数,均可以化为幂函数和正余弦函数的幂次的乘积这种标准形式,那么Term就相应拥有了系数、幂函数及正余弦函数的指数这四个属性。除了正则表达式匹配和求导规则有一些变化,其他与第一次做业并没有太大区别。

值得注意的是,此次HashMap的Key我采用的是将幂函数及正余弦函数的指数拼接成一个字符串,并在相邻指数之间用"|"分隔。如此一来,即可省去重写equals()和hashcode()的麻烦,而后像第一次做业同样合并同类项。

程序结构分析

程序结构大致相似于第一次做业。

度量分析

合法性判断的方法仍是有些冗长了,多是由于其中包含了一些对输入进行预处理的操做,能够考虑将这部分单独抽离出来。

关于BUG

在吸收了上一次做业的教训后,我舍弃了替换捕获组方法,而是根据本次匹配起点是否为上一次匹配的终点,以及最后一次匹配的终点是否为字符串的末尾,来判断输入的合法性。最终在公测和互测中都未发现bug。

发现别人程序的bug依然采用上一次做业的方法,果真又查出了很多bug,大可能是对于非法的输入没有输出WRONG FORMAT!

第三次做业

第三次做业须要完成的任务为包含简单幂函数和简单正余弦函数的导函数的求解(支持因子嵌套在三角函数因子中)。

思路

正如以前所说的,套公式的作法是没法解决任意层嵌套的问题的,所以整个程序架构必须重构。在这里我采用的是递归降低的方法,规定好每种函数、组合的求导法则,将嵌套的内容看作一个总体,留给下一层递归处理,每次只判断当前层的合法性并进行相应的求导。

在优化方面,仅仅是剔除了冗余的0、一、括号、正负号等等,在同类项合并和恒等变换方面未能进行更多的处理,其缘由和求导方法的返回值有关。因为我在一开始设计的时候就直接把每一个因子的求导结果看成字符串保存,因此整个表达式求导的过程就至关因而字符串按必定规则转化和拼接的过程。这样作的好处在于简答、直观、不易出错,但弊端也很明显,那就是很难作进一步优化,由于对字符串中的内容是没法进行插入、删除、替换、排序或合并等操做的,除非像读取输入那样再一次对字符串进行分析。因为时间有限,我没有那样作。其实更好的方法是将求导的结果也做为某一个类的实例,方便对其作进一步的处理。

程序结构分析

如图所示,本次做业我用到了面向对象中的继承,有必要这样作的缘由有三:

  1. 每一个子类都须要toString()方法,以返回该项因子自身的字符串。因为这个方法对于每种因子都同样,因此应该在父类中统一实现。
  2. 每一个子类都须要实现各自的diff()方法,由于每种函数或组合都有本身的求导规则。
  3. 每一个子类都继承于Factor类使得不一样类型的因子能够放在一个ArrayList中一块儿处理。

至于Term,它并非因子,只是Expr的组成部分,因此单独成为一类。

经过继承这种面向对象的思惟方式,咱们程序的结构更加清晰,功能更增强大,最重要的是,拥有了可扩展性。

度量分析

果真越复杂的程序,要作到均衡控制每一个方法的复杂度就越难。主要的问题依然出在输入分析、预处理以及合法性判断上,尽管此次我将他们分离了开来。至于有没有必要以及如何进一步拆解,还有待研究。

关于BUG

本次做业依然没有被查出任何bug。

在查别人的bug方面,为了解放劳动力,此次我选择写脚本进行批量测试。我用到了Python中一个强大的第三方包——<a href="https://www.sympy.org/en/index.html" target="_blank">SymPy</a>,能够进行复杂表达式的求导运算以及判断两式是否等价,这也是个人"简化版评测机"的主要原理,即将Java程序的输出与SymPy的输出进行比对。显然,该评测机没法对非法输入给出评判。而测试用例自己依然是我自测时手动构造的测试集,按行读取即可进行批量测试,威力极大,效率奇高。

总结与建议

第一单元整体仍是比较入门的,重点在于熟悉Java语法特性、创建面向对象思惟,为往后的多线程编程打好基础。

对于我我的而已,学习了Java正则表达式、BigInteger类、HashMap、重写与重载、继承与接口等等技术,而且慢慢开始理解了层次化架构和面向对象技术的优越性。整体感受Java仍是比C/C++要方便的多,基本上只要是你能想到的功能,总有类或者第三方包能为你提供解决方案。而这就须要咱们有查找资料自学的能力,不管是看技术博客仍是阅读官方文档,再加上本身多动手实践,相信很快就能掌握一项新的技术,并在做业中发挥其功能。

此外,经过Checkstyle对代码风格进行规范也是比较有效的措施,基本上不会再写出会使人抓狂的代码了。可是其中每行最多80个字符的限制我实在以为不太合理,尤为是涉及正则表达式的时候,每每是硬生生地把一句逻辑连贯的代码拆成多行。我的认为在阅读代码时,莫名其妙的换行比一行稍长的代码体验更糟。建议将每行的字符数上限改成100~120。

对于面向对象的理解可能仍是只是停留在表层,甚至会有一些误解,这就须要进一步的学习和练习,在一次次实践中总结经验与教训。但愿之后能写出更加OO的程序。

相关文章
相关标签/搜索