目录
第一次作业
-
作业分析
本次作业任务:简单多项式导函数的求解。
经历第一单元的
摧残洗礼之后,再来看第一次作业的要求确实很水。由于表达式中只存在幂函数且每一项遵循 a*x**b 的格式,同时也不用考虑WRONG FORMAT!的情况,我们的工作就显得十分轻松了。- 去除多余的空白符,并且整理多余的加减号。
- 使用正则解析表达式,并且创建实例化对象。
- 根据求导规则进行求导。
- 优化求导之后得到的表达式,输出。
其中对于输出表达式的优化策略主要包括以下:
- 处理系数1、-1,处理指数0、1。
- 合并同类项。
- 根据系数大小排序,保证正数在前。
-
UML图
本次作业,为保证后续的迭代工作,建立了Item抽象类,并设立了求导抽象方法。但是由于对后续迭代的情况考虑不周,第二次作业还是选择了重构。
度量分析
Method | ev(G) | iv(G) | V(G) |
---|---|---|---|
Item.checkItem(Item,ArrayList |
4 | 5 | 6 |
Item.fervation() | 1 | 1 | 1 |
Item.getCoef() | 1 | 1 | 1 |
Item.getIndex() | 1 | 1 | 1 |
Item.getItem(String,ArrayList |
1 | 1 | 1 |
Item.setCoef(BigInteger) | 1 | 1 | 1 |
Item.setIndex(BigInteger) | 1 | 1 | 1 |
PowerFunc.PowerFunc(String,String,ArrayList |
1 | 1 | 1 |
PowerFunc.fervation() | 1 | 1 | 1 |
PowerFunc.getPowerFunc(String,ArrayList |
1 | 2 | 4 |
PowerFunc.toString() | 3 | 3 | 3 |
StrProcess.inProcess(String) | 1 | 1 | 1 |
StrProcess.outProcess(String) | 1 | 1 | 1 |
StrProcess.strReplace(String,String,String) | 1 | 1 | 1 |
Task1.main(String[]) | 2 | 4 | 4 |
Class | OCavg | WMC |
---|---|---|
Item | 1.57 | 11 |
PowerFunc | 2.25 | 9 |
StrProcess | 1 | 3 |
Task1 | 4 | 4 |
从上面两张度量表可以看出,在第一次作业中,程序的设计仍存在很大的问题,有的内部方法复杂度较高,可 维护程度较低,且耦合度较高,并不利于后续的迭代开发。同时,通过整个架构也能很明显的看出这是一个面 向过程的程序。
-
测试&BUG&互测
- 在完成程序的设计以及实现之后,就是进行数据的测试,寻找bug:
- 测试指导书给出的样例。
- 测试数据范围的特殊情况、边界条件。
- 结合python、批文件处理进行数据的覆盖测试。
- 得益于课下较全面的测试,本次作业在中测、强测、互测中均为发现bug。但是在性能优化方面,由于忘记省略系数为0的项,导致部分性能分白给,可惜可惜。
- 在进行互测时,基本步骤与自我的bug检测相差无二。在找到bug后,也会返回源码检测bug是否是同质的,避免多次提交同质bug,同时也能拜读其他同学的代码,学会一些神奇操作。
- 在完成程序的设计以及实现之后,就是进行数据的测试,寻找bug:
第二次作业
-
作业分析
本次作业任务:包含简单幂函数和简单正余弦函数的导函数的求解。
很显然,第二次作业是第一次作业的一个简单升级,引入了简单的正余弦函数以及WRONG FORMAT!的判定。其实关于数据的读取,在已有第一次作业的经验基础之上,很容易想到用四元组的方法对表达式进行解析生成对应四元组并进行求导输出。每一项我们都可以通过简单的合并处理为a*x**b*sin(x)**c*cos(x)**d的形式,即通过(a, b, c, d)对其进行表示。至于WRONG FORMAT!的判断,在前期,我采取通过穷举所有情况进行判别,却总是觉得心理不踏实,担心遗漏某些特殊情况。之后,通过与同学交流,以及助教的提示,明白了我不知道,就是我知道了的人生哲理。通过指导书的形式化表达建立规范表达式的正则进行判断,轻松又省事orz。
通过正则进行WRONG FORMAT!的判断。
- 去除多余的空白符,并且整理多余的加减号。
- 使用正则解析表达式,并且创建实例化对象。
- 根据求导规则进行求导。
优化求导之后得到的表达式,输出。
由于本次作业涉及到了三角函数,尝试通过dfs、熔断等辅助手段进行优化,但是由于课下测试并不全面,只好采用最初始的简单优化。
处理系数1、-1,处理指数0、1。
合并同类项。
cos(x)^2+sin(x)^2=1
-
UML图
这是我基于上述解题步骤的代码实现。很显然的是,有着第一次作业的影子,但是并不是第一次作业的简单拓展。通过StrProcess类对表达式进行WRONG FORMAT!的判断,以及表达式的解析,建立Item项,同时再对Item项进行函数的读取判断,建立继承于Function类的TrigoFunc和PowerFunc的实例化对象,实现求导输出的功能。但是由于代码实现方法采取是四元组的方法,并不具备后续迭代开发的功能。
度量分析
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Item.Item() | 1 | 1 | 1 |
Item.Item(BigInteger,BigInteger,BigInteger,BigInteger) | 1 | 1 | 1 |
Item.feviration(ArrayList |
1 | 1 | 1 |
Item.feviration(ArrayList |
1 | 1 | 1 |
Item.getCoeff() | 1 | 1 | 1 |
Item.getCosFactor() | 1 | 1 | 1 |
Item.getPowFactor() | 1 | 1 | 1 |
Item.getSinFactor() | 1 | 1 | 1 |
Item.itemAdd(ArrayList |
4 | 2 | 4 |
Item.itemCompare(Item) | 5 | 10 | 10 |
Item.itemComplete(BigInteger,BigInteger,String) | 1 | 4 | 4 |
Item.setCoeff(BigInteger) | 1 | 1 | 1 |
Item.toString() | 5 | 4 | 5 |
PowerFunc.PowerFunc(BigInteger,BigInteger) | 1 | 1 | 1 |
PowerFunc.PowerFunc(PowerFunc) | 1 | 1 | 1 |
PowerFunc.ferivation() | 1 | 1 | 1 |
PowerFunc.getCoeff() | 1 | 1 | 1 |
PowerFunc.getIndex() | 1 | 1 | 1 |
PowerFunc.setCoeff(BigInteger) | 1 | 1 | 1 |
PowerFunc.setIndex(BigInteger) | 1 | 1 | 1 |
PowerFunc.toString() | 4 | 4 | 4 |
StrProcess.checkIn(String) | 3 | 5 | 5 |
StrProcess.readCoeff(String,Item) | 1 | 2 | 2 |
StrProcess.readItem(String,ArrayList |
1 | 2 | 2 |
StrProcess.readPower(String,Item) | 1 | 3 | 3 |
StrProcess.readTrigo(String,Item) | 1 | 3 | 3 |
StrProcess.strInPlace(String) | 1 | 1 | 1 |
StrProcess.strOutPlace(String) | 1 | 1 | 1 |
Task2.main(String[]) | 1 | 1 | 1 |
TrigoFunc.TrigoFunc(BigInteger,BigInteger,String) | 1 | 1 | 1 |
TrigoFunc.TrigoFunc(TrigoFunc) | 1 | 1 | 1 |
TrigoFunc.ferivation() | 1 | 2 | 2 |
TrigoFunc.getCoeff() | 1 | 1 | 1 |
TrigoFunc.getIndex() | 1 | 1 | 1 |
TrigoFunc.getType() | 1 | 1 | 1 |
TrigoFunc.setCoeff(BigInteger) | 1 | 1 | 1 |
TrigoFunc.setIndex(BigInteger) | 1 | 1 | 1 |
TrigoFunc.toString() | 3 | 3 | 3 |
Class | OCavg | WMC |
---|---|---|
Function | n/a | 0 |
Item | 2.17 | 26 |
PowerFunc | 1.38 | 11 |
StrProcess | 2.43 | 17 |
Task2 | 1 | 1 |
TrigoFunc | 1.33 | 12 |
从度量表的分析来看,对于大部分方法我已经做到了一定程度上的低耦合、高内聚,但是仍存在一些耦合度较高的方法。同时,也可以看出一些方法的复杂度较高,可维护性较低,这也是值得引起注意并加以改正的地方。代码的设计实现已经逐步向面向对象靠拢,但也不免其中参杂一些虚假的面向对象。
-
测试&BUG&互测
- 在完成程序的设计以及实现之后,就是进行数据的测试,寻找bug:
- 测试指导书给出的样例。
- 测试数据范围的特殊情况、边界条件。
- 结合python、批文件处理进行数据的覆盖测试。
- 由于本次测试引入了WRONG FORMAT!,对于该部分进行大量的手动数据测试。
- 得益于课下较全面的测试,本次作业在中测、强测、互测中均为发现bug。但是在性能优化方面,由于优化版本的测试不足,并未提交优化版本,而是提交了优化不足但是经过大量测试的初始版本,只做了最为简单的优化,导致部分性能分白给5分,实在是可惜可惜。
- 在进行互测时,基本步骤与自我的bug检测相差无二。在找到bug后,也会返回源码检测bug是否是同质的,避免多次提交同质bug,同时也能拜读其他同学的代码,学会一些神奇操作。同时,结合课下测试的大量保留数据进行WRONG FORMAT!的针对性测试。
- 在完成程序的设计以及实现之后,就是进行数据的测试,寻找bug:
第三次作业
-
作业分析
本次作业任务:包含简单幂函数和简单正余弦函数的导函数及其组合的求解。
第三次作业在第二次作业的基础上,引入了嵌套表达式因子的概念。显然无法再套用前两次作业简单的正则表达式进行解析。很容易的,想到了采用递归方法对表达式进行解析,而表达式因子本质上也只是因子,再借鉴第二次作业的方法将每一层次的表达式因子用x进行替换,即可对每一层套用常规正则表达式进行解析,逐层深入,进而实现对整个表达式的解析。
- 通过递归替换+正则表达式对表达式进行WRONG FORMAT!判断。
- 去除表达式中多余的空白符,将每一项进行规范化处理。
- 利用递归+正则对表达式进行解析,并建立实例化对象。
- 递归求导,优化输出。
由于本次作业支持表达式因子的嵌套模式,所以对于化简存在一定困难,本次作业仅打算在保证正确的情况下,进行简单的优化处理。
- 去除多余的括号。
- 合并项中的常数与幂函数。
-
UML图
本次作业是一次彻彻底底的重构,通过前文的分析,我在这次作业中采用了Exp,Term,Factor的层次结构。对于每一个exp,它是由若干个Term构成。对于每个Term,其基本形式是**a*x**b*sin(...)**c*cos(...)**d*(...)*....。显然,对于每一项可以定义其系数,以及幂函数的指数,且保存三角因子、表达式因子的形式,生成子类Factor。Factor**的内容亦是一个表达式。这样,我们得到了一个由这三个类构成的层次结构。剩下的,就是对每一项的求导、优化、输出。
-
度量分析
Method ev(G) iv(G) v(G) Exp.Exp() 1 1 2 Exp.clone() 1 2 2 Exp.derivation() 1 2 2 Exp.getArrayTerm() 1 1 1 Exp.toString() 1 3 5 Factor.Factor() 1 1 1 Factor.Factor(String,BigInteger) 1 1 1 Factor.clone() 1 1 1 Factor.derivation(ArrayList ,Term,int) 1 3 3 Factor.getExp() 1 1 1 Factor.setExp(Exp) 1 1 1 Factor.toString() 6 4 7 ParseExp.getExp(String) 1 2 2 ParseExp.getFactor(String) 1 1 1 ParseExp.getFactor(String,BigInteger,String) 1 1 1 ParseExp.getTerm(String) 1 7 7 StrProcess.replaceBracket(String) 1 1 6 StrProcess.strExpReplace(String) 1 1 1 StrProcess.strInReplace(String) 1 1 1 StrProcess.strOutReplace(String) 1 1 1 Task3.main(String[]) 2 2 2 Term.Term() 1 1 1 Term.clone() 1 2 2 Term.derivation(ArrayList ) 1 2 2 Term.getArrayFactor() 1 1 1 Term.getCoeff() 1 1 1 Term.getIndex() 1 1 1 Term.setCoeff(BigInteger) 1 1 1 Term.setIndex(BigInteger) 1 1 1 Term.toString() 4 7 11 WrongFormat.checkWF(String) 2 1 2 WrongFormat.dfsCheck(String,String) 6 3 6 Class OCavg WMC Exp 2 10 Factor 2 14 ParseExp 2.75 11 StrProcess 2.25 9 Task3 2 2 Term 2.33 21 WrongFormat 4 8 从以上的表格分析,程序的复杂度主要出现在toString以及WrongFormat的判断方法,说明在程序的设计以及实现上,仍存在一定的缺陷,缺少对代码可维护性的考量。同时,对于少部分的方法耦合度仍然很高,内聚性也不是很高。代码的可迭代性仍然很差。
-
测试&BUG&互测
- 由于本次引入了嵌套的实现功能,对于数据生成器的搭建并不是很理想,生成的数据也比较弱。所以本次测试采取了大量手动数据测试方法,遍历各种极限情况:数据范围,嵌套层次...。同时,辅佐以生成的大量简单测试数据。
- 在本次作业的中测、强测、互测过程中,程序也未被查出任何bug。只是由于未消去部分的多余括号,导致性能分仍然白给,可惜可惜。
- 在互测过程中,主要使用了课下自我测试保留的数据进行大规模对拍测试,同时也尝试着通过阅读代码进行bug查找(
看得脑壳疼)。
总结
回顾第一单元的作业完成情况,确实暴露出了大大小小的问题,不仅仅是程序的设计实现上,更是在一些细节上。
- 过于在意作业的完成,忽略了面向对象思维的培养,固守于面向过程的设计思路,只求完成作业。
- 仅仅只考虑了程序的正确性,对于程序的性能并未投入过多的精力,导致性能分白给。
- 在互测过程中,对评测机有较大的依赖,并没有回归到代码本身,吸收其他同学的精华。
- 囿于旧知识,缺乏对新知识的主动学习。(三次作业只使用了ArrayList容器,非常非常糟糕的习惯。)
这些都是值得重视的问题,需要及时纠正。当然,也收获了许多并不局限于java编程的知识。
体会
在正式接触OO之前,曾在某APP浏览过关于这门课一些或好或坏的评价,有人称赞这是一门对编程能力有着实质性提高的课程,也有人戏称这是一门面向运气编程的课程。当时还不曾接触,也就只是当个瓜默默吃掉。
至于现在,在完成了第一单元的学习之后,倒是有了不少自己的体会。我不敢妄加评论这门课是好是坏,但是至少我觉得这门课是有趣的。在我看来,这门课程就像是刷怪练级打boss。每周一个boss,想要通关,就得平日里自己多肝一点(至少不能氪金过关qwq),刷刷怪呀,练练级呀,多整点技能点,多点几个技能。虽然这样的练级日常比不上网游那般刺激、精彩,但是对于自己的提升肯定是实打实的。当然,有的时候自己操作不行,就得去论坛里找找攻略帖,或者抱紧大佬的大腿交流交流通关技巧。双周的周三更是极致的享受(摸鱼),大佬主动分享黑科技、攻略、奇巧淫技……我挺喜欢这样的氛围,一种讨论交流、思想碰撞的氛围,而非为学而学的机械过程。至于互测,我倒是觉得别把它看得太过那啥,与其说这是一个