第一次作业
需求分析
第一次作业只涉及到幂函数的求导,输入表达式是多个幂函数的和,要求输出求导后的表达式。
设计思路
第一次作业难度不是很大,由于之前我没有面向对象的设计经验,而字符串处理又是大一面向过程的写法中经常遇到一类问题,所以尽管我努力写的oo,但是最后仍然很面向过程。整个程序分为两个类,一个是PolyCal作为主类,主要负责处理输入并且控制所有的幂函数,另一个是Pow类,负责存储幂函数的信息。由于相同指数的幂函数可以合并同类项,所以我在Pow类中用了map容器,把每一个幂函数的指数作为Key,把系数作为Value,当添加每一项时,直接进行合并同类相的操作。最后遍历所有Pow的对象完成输出。
测试相关
我的程序在强测中没有出现bug,在互测中也没有出现bug。
在互测中,我发现了其他人的bug。其他人的bug主要有两个方面,一个是对于1和-1的处理,在其他人的程序中,当要输出的指数是1时,有可能会出现类似于-2*x**的情况,经过阅读代码发现,他本意是想在指数为1减少输出长度,但是处理不够恰当。另一个bug是在指数过高的情况下,有可能会输出^而不是**。
UML类图
第二次作业
需求分析
第二次作业涉及到了函数乘积的求导,并且加入了三角函数和WRONG FORMAT的判断,跟第一次作业相比难度有所增加。
设计思路
在设计第二次作业架构时,由于第一次作业的架构不是很好,所以我没有在第一次作业的基础上进行扩展,而是重写了所有代码,共分为6个类。我先把整个表达式中的每一项提出来,然后用四个参数存储每一项的信息,分别为系数,幂函数指数,正弦函数指数,余弦函数指数,在每提取一项之后,在项内运用正则表达式提取出四个参数,并存储在主类的容器中。为了得到更多的性能分,我提取每一项之后又进行了优化,将部分系数和指数相同的项进行合并。
测试相关
我的程序在强测中没有发现bug,在互测中被发现了一个bug。互测中的bug是由于优化导致的。在这段(修改后)代码中,我每要添加一个项,就需要判断这个项是否在容器中,以及这个项的系数是否为0,当系数不为0并且不在容器中时就添加进容器,于是我就把这两个条件用&&连接起来写在了一个if条件中,导致当系数为0时,若不满足这个条件,则被错误地添加到容器中,造成后面的错误。以前我也写出过类似的bug,这种bug在阅读自己的代码和测试中很难发现,所以以后更应该牢记自己犯下的错误,更注意分支语句条件的书写。
在互测中,我发现了其他人的bug。经过了第一次的互测,我发现了一些容易犯的错误,比如对1和-1的处理上。果然,在构造了几组数据后我发现,在某些情况下,其他人的程序会输出x1,以及输出以-为结尾的表达式。此外,还有一类错误是对WRONG FORMAT的判断,当指数是10000时,会输出WRONG FORMAT,这也给了我警示,在阅读指导书时一定要仔细,千万不能看错了条件。
public void add(BigInteger coef,BigInteger pow,BigInteger sin,BigInteger cos) { AnsTerm ansTerm = new AnsTerm(coef, pow, sin, cos); if (coef.compareTo(new BigInteger("0")) != 0) { if (this.map.containsKey(ansTerm)) { map.put(ansTerm, map.get(ansTerm).add(coef)); } else { this.map.put(ansTerm, coef); } } }
UML类图
代码度量
method | ev(G) | iv(G) | v(G) |
AnsTerm.AnsTerm(BigInteger,BigInteger,BigInteger,BigInteger) | 1.0 | 1.0 | 1.0 |
AnsTerm.canMerge(AnsTerm) | 3.0 | 3.0 | 5.0 |
AnsTerm.equals(Object) | 3.0 | 2.0 | 5.0 |
AnsTerm.getCoef() | 1.0 | 1.0 | 1.0 |
AnsTerm.getCos() | 1.0 | 1.0 | 1.0 |
AnsTerm.getPow() | 1.0 | 1.0 | 1.0 |
AnsTerm.getSin() | 1.0 | 1.0 | 1.0 |
AnsTerm.hashCode() | 1.0 | 1.0 | 1.0 |
AnsTerm.setCos(BigInteger) | 1.0 | 1.0 | 1.0 |
AnsTerm.setSin(BigInteger) | 1.0 | 1.0 | 1.0 |
Answer.add(BigInteger,BigInteger,BigInteger,BigInteger) | 1.0 | 3.0 | 3.0 |
Answer.Answer() | 1.0 | 1.0 | 1.0 |
Answer.getAns() | 1.0 | 3.0 | 4.0 |
Answer.getFactor(AnsTerm) | 1.0 | 10.0 | 11.0 |
Answer.optimize() | 6.0 | 9.0 | 9.0 |
Check.Check(String) | 3.0 | 7.0 | 10.0 |
Check.getIsCorrect() | 1.0 | 1.0 | 1.0 |
Check.judge() | 1.0 | 5.0 | 9.0 |
Check.setIsCorrect(int) | 1.0 | 1.0 | 1.0 |
Check.split() | 1.0 | 9.0 | 10.0 |
Opti.add(AnsTerm) | 1.0 | 1.0 | 1.0 |
Opti.getList() | 1.0 | 1.0 | 1.0 |
Opti.Opti() | 1.0 | 1.0 | 1.0 |
PolyCal.main(String[]) | 6.0 | 13.0 | 13.0 |
Term.checkCoef() | 3.0 | 5.0 | 5.0 |
Term.checkCos() | 1.0 | 3.0 | 3.0 |
Term.checkPow() | 1.0 | 5.0 | 5.0 |
Term.checkSin() | 1.0 | 3.0 | 3.0 |
Term.getCoef() | 1.0 | 1.0 | 1.0 |
Term.getCos() | 1.0 | 1.0 | 1.0 |
Term.getPow() | 1.0 | 1.0 | 1.0 |
Term.getSin() | 1.0 | 1.0 | 1.0 |
Term.Term(String) | 4.0 | 8.0 | 9.0 |
Term.work() | 1.0 | 1.0 | 1.0 |
第三次作业
需求分析
第三次作业涉及到幂函数,正余弦函数的组合及嵌套的求导,难度较大。
设计思路
这次作业对我来说是个不小的挑战,整体的复杂性和对架构的要求都远高于前两次作业,这次我仍然没有用前一次的代码,而是选择了重写。我从数据之间的包含关系入手,把输入分为表达式,项,因子,每一层都对下一层有着包含关系,然后在处理输入时从上到下分解,在求导时从下到上求导,在对因子求导时,由于因子本身可能是一个表达式,所以在因子求导中又使用了表达式类,形成递归。对于WRONG FORMAT的判断,由于这次加入了非法的空白字符,所以我先单独判断空白字符的合法性,再对整个表达式去掉所有空白字符,再由上向下逐步拆解,依次运用正则表达式判断合法性。对于输出的优化,我主要是把重点放在了对于指数和系数是0和1的优化,以减少输出的长度。
测试相关
我的程序在强测中没有发现bug,在互测发现了一个bug以及TLE的情况。bug是由于我在对每一项预处理时产生的,在预处理时,我合并了每一项的所有幂函数因子,将他们的指数相加,然后再把其他因子还原。但是当这个项没有其他因子,没有其他常数,只有x**0时,可能会出现问题,因为0次方被我优化掉了,导致表达式不完整,从而产生问题。
在互测中,我找到了其他同学的几个bug。一个是TLE的问题,当括号嵌套过多时,可能会导致TLE的情况发生。另一个是sin((0))导致的,原因应该是对方在优化时把0优化掉了导致输出了sin(())。
UML类图
代码度量
method | ev(G) | iv(G) | v(G) |
CheckString.checkBlank(String) | 11.0 | 18.0 | 25.0 |
CheckString.checkBlank1(String) | 13.0 | 15.0 | 20.0 |
CheckString.CheckString() | 1.0 | 1.0 | 1.0 |
CheckString.getSet(String) | 1.0 | 6.0 | 6.0 |
CheckString.initTerm(String) | 8.0 | 6.0 | 10.0 |
CheckString.judgeFactor(String) | 17.0 | 23.0 | 29.0 |
CheckString.judgePoly(String) | 9.0 | 13.0 | 17.0 |
CheckString.judgeTerm(String) | 12.0 | 10.0 | 15.0 |
Factor.derivative(String) | 9.0 | 16.0 | 16.0 |
Factor.Factor(String) | 1.0 | 2.0 | 2.0 |
Factor.getFactor() | 1.0 | 1.0 | 1.0 |
Factor.powDeri(String) | 6.0 | 8.0 | 9.0 |
Factor.triDeri(String) | 7.0 | 7.0 | 11.0 |
MainClass.main(String[]) | 4.0 | 4.0 | 4.0 |
MainClass.setIsCorrect(int) | 1.0 | 1.0 | 1.0 |
Poly.derivative() | 1.0 | 3.0 | 3.0 |
Poly.getPoly() | 1.0 | 1.0 | 1.0 |
Poly.getTerm() | 5.0 | 15.0 | 15.0 |
Poly.judgePoly() | 1.0 | 1.0 | 1.0 |
Poly.Poly(String) | 1.0 | 1.0 | 1.0 |
Poly.removeBlank() | 1.0 | 1.0 | 1.0 |
Simplify.getFactor(String) | 1.0 | 15.0 | 15.0 |
Simplify.rmBracket(String) | 7.0 | 7.0 | 8.0 |
Simplify.rmSign(String) | 2.0 | 2.0 | 2.0 |
Simplify.Simplify() | 1.0 | 1.0 | 1.0 |
Simplify.simplifyTerm(String) | 1.0 | 9.0 | 12.0 |
Term.derivative() | 1.0 | 8.0 | 10.0 |
Term.getFac() | 1.0 | 15.0 | 15.0 |
Term.getTerm() | 1.0 | 1.0 | 1.0 |
Term.init() | 4.0 | 5.0 | 6.0 |
Term.Term(String) | 1.0 | 1.0 | 1.0 |
第一单元总结
第一单元的三次作业都已经结束了,总的来说收获不小,有一些令我满意的地方,但也有很多不满意的地方。
首先,三次难度递增的作业让我更加熟悉java的语法,并且逐渐了解oo的思想。虽然每次作业都会耗费很多的精力去思考,去测试,但是在三次强测中都没有出现bug。
其次,在互测中让我收获很多。在测试他人的程序时,我发现了许多bug,并且我也能理解这些bug产生的原因,所以一次次的测试也使我加深了印象,在以后的作业中可以尽量避免这些错误的产生。而在我的程序被测试的过程中,其他人也发现了一些我的程序的问题,而我作为编程者,有时却很难发现自己的bug,所以发现bug,修复bug的过程也是我不断完善自己的过程,我也要在问题出现之后去总结和反思。
最后,我发现我对于面向对象思想的理解不够深刻。在三次作业中,我都尽量想写出oo风格的代码,但是最后发现整体的代码还是很面向过程的,而且代码的扩展性不是特别高。希望在后面的作业中,我能够更深刻的理解面向对象的编程思想,写出更具有oo风格的代码。