目录
-
1.第一单元三次作业分析
-
第一次作业
-
第二次作业
-
第三次作业
-
-
2.简易的评测装置
-
结果评测
-
数据生成
-
-
3.心得体会
-
关于面向对象
-
关于设计模式
-
-
1.程序结构分析
第一次作业
思路:
-
读入表达式后首先对表达式进行化简,由于本次不会出现不合法的数据,我选择了利用String.replaceAll方法去除掉了所有的空格和制表符,然后将相连的+-,-+,++,--进行了化简。
-
利用正则表达式
regex = "[\\+-][0-9]*(\\*)?x?(\\*{2}[\\+-]?[0-9]+)?"
提取出每一项 -
利用
HashMap
存储项的指数系数的对应关系,边读入,边化简。 -
求导后进行格式化输出,优化系数是
+-1
的情况,同时对指数是1的情况进行化简输出。
UML类图:
Metrics:
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
ComputePoly.compute() | 3.0 | 2.0 | 3.0 |
ComputePoly.ComputePoly() | 1.0 | 1.0 | 1.0 |
ComputePoly.printString() | 4.0 | 11.0 | 11.0 |
MainClass.main(String[]) | 1.0 | 1.0 | 1.0 |
ParseExp.ParseExp(String) | 3.0 | 5.0 | 7.0 |
Poly.addPoly(BigInteger,BigInteger) | 1.0 | 2.0 | 2.0 |
Poly.getPoly() | 1.0 | 1.0 | 1.0 |
Poly.Poly() | 1.0 | 1.0 | 1.0 |
SimplifyExp.getExpression() | 1.0 | 1.0 | 1.0 |
SimplifyExp.SimplifyExp(String) | 1.0 | 2.0 | 3.0 |
Total | 17.0 | 27.0 | 31.0 |
1,2次作业沿用了基本相同的套路,整体复杂度都不高,飘红的部分主要是解析表达式
公测与互测:
-
公测 : 没有出现Bug
-
互测: 也没有出现Bug,找到了别人一个没有输出结果的小bug,对方会在最终结果为0时没有输出
第二次作业
思路 :
采取了与第一次作业十分类似的做法,采用匹配项的方法对长表达式进行解析依然遵循小正则匹配项的方式,与第一次的区别之处在于以下几点
-
由于出现了sin和cos项,我选择采用三元组的方式来存储一个项中幂函数、
sin(x)
和cos(x)
的系数,三元组采取了建立了一个新的Facor
类来解决。 -
在解析表达式时,采用了分解因子的方式,首先构造出最基本的因子,最后再由因子组合成项,降低了写正则表达式出错的可能性。
-
判断WF时,我的正则表达式能够正确匹配符合要求的项。在扫描输入的表达式时,检查上次匹配的末尾是否是下一次匹配的开始部分,如果有非法字符加入,我的程序也能够进行很好的判断。
while (m.find()) {
if (m.start() != end) {
System.out.println("WRONG FORMAT!");
return;
} else {
int start = m.start();
end = m.end();
if (!term.addTerm(input.substring(start,end))) {
System.out.println("WRONG FORMAT!");
return;
}
}
}
if (end != input.length()) {
System.out.println("WRONG FORMAT!");
return;
}
由于采用了与第一次作业相似的架构,本次作业只是第一次作业稍作修改的复杂版本,基本没有大的重构现象,但是这也为我第三次作业的紧张重写埋下了伏笔。
UML类图:
Metrics
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Factor.equals(Object) | 3.0 | 4.0 | 6.0 |
Factor.Factor(BigInteger,BigInteger,BigInteger) | 1.0 | 1.0 | 1.0 |
Factor.getCosIndex() | 1.0 | 1.0 | 1.0 |
Factor.getFxIndex() | 1.0 | 1.0 | 1.0 |
Factor.getSinIndex() | 1.0 | 1.0 | 1.0 |
Factor.hashCode() | 1.0 | 1.0 | 1.0 |
Factor.toString() | 1.0 | 9.0 | 9.0 |
HandleExp.handle() | 5.0 | 5.0 | 5.0 |
HandleExp.HandleExp(String) | 1.0 | 2.0 | 3.0 |
MainClass.main(String[]) | 1.0 | 1.0 | 1.0 |
Term.addHashmap(HashMap,Factor,BigInteger) | 2.0 | 2.0 | 3.0 |
Term.addTerm(String) | 6.0 | 13.0 | 14.0 |
Term.derivation() | 1.0 | 2.0 | 2.0 |
Term.derivation(Factor) | 2.0 | 5.0 | 5.0 |
Term.Term() | 1.0 | 1.0 | 1.0 |
Term.toString() | 10.0 | 8.0 | 11.0 |
Total | 38.0 | 57.0 | 65.0 |
Average | 2.375 | 3.5625 | 4.0625 |
公测与互测:
-
公测 : 本着
不化简就不会出bug的原则(其实是去忙别的事情了),本次公测没有出现任何问题 -
互测 : 血泪教训。上次我hack别人的方式被用到了自己的身上,因为求导最后,
Hashmap
为空,结果应为为0,我没有输出而喜提一血,以后一定要充足的做好测试。忙里偷闲造出来的评测机发挥了自己的作用,我主要采用了自己去制造特殊数据,边界数据的方法对小组内的人进行hack,最后发现了4个非同质的bug,还成功的hack到了我亲爱的舍友zzy。
第三次作业
思路
第三次作业的难度有了显著的提升,经过仔细的思考后我发现我之前采取的结构不能很好的解决表达式嵌套的问题,因而我果断的进行了重构。
-
首先确定了采用表达式树的方法对输入的表达式进行解析存储。根据指导书的提示,我建立了
Derivable
接口,并分别构造Operator
和Factor
抽象类,让他们的子类都实现Derivable
-
Factor
中存在常数因子,幂函数因子,sin嵌套型因子
,cos嵌套型因子
,分别实现他们的求导方法和表示他们原本的值的toString
方法。Operator
类中实现了乘法,加减法和嵌套操作,并实现了每种操作对应的求导方法。 -
采用字符自动机的方式,对输入的表达式进行解析。对sin,cos内部的表达式进行递归解析。最终只需对表达式树的根节点进行求导即可得到整个表达式的导数。
这种思路在我看来很容易理解也不难实现,但是对于化简操作就很困难了
Metrics
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
FactorConstant.add(FactorConstant) | 1.0 | 1.0 | 1.0 |
FactorConstant.derivation() | 1.0 | 1.0 | 1.0 |
FactorConstant.equals(Object) | 3.0 | 2.0 | 4.0 |
FactorConstant.FactorConstant(String) | 1.0 | 1.0 | 1.0 |
FactorConstant.getValue() | 1.0 | 1.0 | 1.0 |
FactorConstant.hashCode() | 1.0 | 1.0 | 1.0 |
FactorConstant.mul(FactorConstant) | 1.0 | 1.0 | 1.0 |
FactorConstant.setValue(String) | 1.0 | 1.0 | 1.0 |
FactorConstant.sub(FactorConstant) | 1.0 | 1.0 | 1.0 |
FactorConstant.toString() | 1.0 | 1.0 | 1.0 |
FactorCos.derivation() | 3.0 | 3.0 | 4.0 |
FactorCos.equals(Object) | 3.0 | 3.0 | 5.0 |
FactorCos.FactorCos(String,String) | 1.0 | 1.0 | 1.0 |
FactorCos.getContent() | 1.0 | 1.0 | 1.0 |
FactorCos.getCosIndex() | 1.0 | 1.0 | 1.0 |
FactorCos.hashCode() | 1.0 | 1.0 | 1.0 |
FactorCos.setContent(String) | 1.0 | 1.0 | 1.0 |
FactorCos.setCosIndex(String) | 1.0 | 1.0 | 1.0 |
FactorCos.toString() | 3.0 | 2.0 | 3.0 |
FactorPower.derivation() | 3.0 | 3.0 | 3.0 |
FactorPower.equals(Object) | 3.0 | 2.0 | 4.0 |
FactorPower.FactorPower(String) | 1.0 | 1.0 | 1.0 |
FactorPower.hashCode() | 1.0 | 1.0 | 1.0 |
FactorPower.mul(FactorPower) | 1.0 | 1.0 | 1.0 |
FactorPower.toString() | 3.0 | 2.0 | 3.0 |
FactorSin.derivation() | 3.0 | 2.0 | 4.0 |
FactorSin.equals(Object) | 3.0 | 3.0 | 5.0 |
FactorSin.FactorSin(String,String) | 1.0 | 1.0 | 1.0 |
FactorSin.getContent() | 1.0 | 1.0 | 1.0 |
FactorSin.getSinIndex() | 1.0 | 1.0 | 1.0 |
FactorSin.hashCode() | 1.0 | 1.0 | 1.0 |
FactorSin.setContent(String) | 1.0 | 1.0 | 1.0 |
FactorSin.setSinIndex(String) | 1.0 | 1.0 | 1.0 |
FactorSin.toString() | 3.0 | 2.0 | 3.0 |
HandleExp.caseEleven(int) | 4.0 | 11.0 | 13.0 |
HandleExp.caseOneThree(int,String,int) | 6.0 | 8.0 | 10.0 |
HandleExp.error() | 1.0 | 1.0 | 1.0 |
HandleExp.handlecontent(String) | 6.0 | 12.0 | 14.0 |
HandleExp.HandleExp(String) | 1.0 | 2.0 | 2.0 |
HandleExp.init() | 1.0 | 2.0 | 3.0 |
HandleExp.insertTree() | 8.0 | 19.0 | 21.0 |
HandleExp.judgeindex(String) | 1.0 | 3.0 | 3.0 |
HandleExp.merge(char) | 1.0 | 3.0 | 3.0 |
HandleExp.mergeLast() | 1.0 | 2.0 | 2.0 |
HandleExp.oprate(char,Derivable,Derivable) | 13.0 | 12.0 | 14.0 |
HandleExp.priority(char) | 5.0 | 1.0 | 6.0 |
HandleExp.setState(int) | 1.0 | 1.0 | 1.0 |
MainClass.main(String[]) | 1.0 | 1.0 | 1.0 |
Operator.getLchild() | 1.0 | 1.0 | 1.0 |
Operator.getRchild() | 1.0 | 1.0 | 1.0 |
Operator.Operator(Derivable,Derivable) | 1.0 | 1.0 | 1.0 |
OperatorBase.derivation() | 3.0 | 3.0 | 3.0 |
OperatorBase.OperatorBase(char,Derivable,Derivable) | 1.0 | 1.0 | 1.0 |
OperatorBase.toString() | 1.0 | 1.0 | 1.0 |
OperatorMulti.addString(String,String) | 5.0 | 7.0 | 8.0 |
OperatorMulti.derivation() | 4.0 | 5.0 | 6.0 |
OperatorMulti.OperatorMulti(Derivable,Derivable) | 1.0 | 1.0 | 1.0 |
OperatorMulti.toString() | 1.0 | 1.0 | 1.0 |
OperatorTao.derivation() | 1.0 | 1.0 | 1.0 |
OperatorTao.OperatorTao(Derivable,Derivable) | 1.0 | 1.0 | 1.0 |
OperatorTao.toString() | 1.0 | 1.0 | 1.0 |
SimplifyExp.error() | 1.0 | 1.0 | 1.0 |
SimplifyExp.Output() | 1.0 | 6.0 | 6.0 |
SimplifyExp.SimplifyExp(String) | 1.0 | 1.0 | 1.0 |
Total | 129.0 | 159.0 | 191.0 |
Average | 2.015625 | 2.484375 | 2.984375 |
字符自动机部分的实现复杂度较高,可以将其中的操作进一步拆解达到解耦的效果
公测与互测:
-
公测 : 本次公测感觉上难度并不是十分的大,我自己考虑的许许多多的WF的情况都没有进行测试,公测没有出现任何bug。
-
互测 : 我在互测中也没有出现任何bug。就我们小组出现的bug情况来看,主要集中在一些同学过度优化,出现死循环的现象。另外就是有些同学在合法输入时出现解析的一些问题,hack了5发之后就此收手。
2.一个十分简易的评测装置
结果评测
在os课上学习了bash的一些操作 之后用了半天的时间自己写了一个较为简单的评测程序,在文件夹中查找程序入口所在的类,使用javac
命令进行编译,将input.txt
中的数据采用从定向的方法作为java
命令的输入,将本次的输入和输出重定向到用于检测的python程序中,使用sympy
库对输入进行求导并采用equals
方法进行正确性的判断,如果你的输出结果没有进行大量额优化,equals方法足以满足本次作业的比对任务。同时,对该程序进行简单的修改即可做到对于整组人的同时测试,反应出他们输出的正确与否,极大地提升了测试的效率。
[简易的评测程序]
数据生成
讨论区的大佬们一般采用python
的xeger
库进行对给定正则表达式的批量数据生成,但是我个人觉得这种生成数据的方法很难真正去随机出一种边界、临界的状态去触发bug,因此我在互测时一般是直接采用我在写代码时想到的可能出问题的数据点,以及一些处于临界值得数据点去进行测试,在这三次作业中还是收获到了比较不错的效果。
3.心得体会
关于面向对象
从pre开始正式接触了面向对象的编程模式 。由于上学期选修了JAVA的一般专业课,JAVA的语法和一些容器的使用在我看来并不是很难上手。但是作为面向对象编程的新手,在本单元三次作业里,虽然我在尽力的去设计、构思,但是由于面向对象经验的不足,我很难能够让每个类都做到高内聚低耦合,很难每个对象都各司其职,在实现自己功能的时候不去干扰别人。 我在调试中深有体会,一个冗长的类出现问题时,我很难正确的定位bug所在的位置。而使用了接口、抽象类等工具,我能更好的发现到底是哪一个类中的哪一个方法出现了问题。如何能够更好的对类进行设计,让其更加符合面向对象的思想,是我以后需要努力的方向。
关于设计模式
简单的使用了工厂模式进行对象的生成,有效的对一些复杂度比较高的类进行了解耦,也使得代码更加容易理解,便于调试和维护。
总结
面向对象的第一单元已经结束了,取得的成绩还是让我很满意的,这三个星期的收获也非常的大。希望在以后的学习中, 总结此次的经验,面向对象,拥抱对象。