前言:
第一单元可以说是我的噩梦开端了,我本人代码基础较差,加上假期没有好好预习,直到第一周的周五才开始着手构思本单元的任务,加之对于面向对象对不够充分的理解,导致了第一单元的前两次作业的溃败.为此我决定认真反思.并在逐步回顾过去三周代码的过程中总结自己的得与失。
-
Homework1分析
总述
本次实验采用了Factor接口,定义了Expr与Var实现了Factor的setPower(基于表达式与简单变量因子共同的power属性),以及合算(calFactor)。本次实验总体思路并不难,对于我个人来说问题在于读取括号之后的词法分析与回退设计(我的方法要求回退),这带来了让我头疼的一系列bug。
Complexity metrics | 周二 | 22 3月 2022 19:12:34 CST | ||
---|---|---|---|---|
Method | CogC | ev(G) | iv(G) | v(G) |
Dan.getDuan() | 0 | 1 | 1 | 1 |
Dan.getNumber(int) | 8 | 1 | 9 | 9 |
Dan.getPos() | 0 | 1 | 1 | 1 |
Dan.getVar() | 11 | 4 | 7 | 8 |
Dan.gonext() | 4 | 2 | 5 | 5 |
Dan.numpos(int) | 5 | 1 | 5 | 7 |
Dan.setDuan(String) | 0 | 1 | 1 | 1 |
Dan.setInput(String) | 0 | 1 | 1 | 1 |
Dan.setPos(int) | 0 | 1 | 1 | 1 |
Expr.addTerm(Term) | 0 | 1 | 1 | 1 |
Expr.calFactor() | 41 | 2 | 13 | 14 |
Expr.iniExpr() | 0 | 1 | 1 | 1 |
Expr.printExpr() | 19 | 1 | 7 | 8 |
Expr.setPower(int) | 0 | 1 | 1 | 1 |
Expr.setTerms(HashSet<Term>) | 0 | 1 | 1 | 1 |
Ju.parseExpr() | 3 | 1 | 4 | 4 |
Ju.parseFactor() | 44 | 6 | 23 | 24 |
Ju.parseTerm(boolean) | 1 | 1 | 2 | 2 |
Ju.setLittle(Dan) | 0 | 1 | 1 | 1 |
MainClass.main(String[]) | 0 | 1 | 1 | 1 |
Term.addFactor(Factor) | 0 | 1 | 1 | 1 |
Term.cal() | 56 | 3 | 14 | 14 |
Term.iniTerm() | 0 | 1 | 1 | 1 |
Term.isNeg() | 0 | 1 | 1 | 1 |
Term.setNeg(boolean) | 0 | 1 | 1 | 1 |
Var.calFactor() | 0 | 1 | 1 | 1 |
Var.getPower() | 0 | 1 | 1 | 1 |
Var.getXishu() | 0 | 1 | 1 | 1 |
Var.setPower(int) | 0 | 1 | 1 | 1 |
Var.setXishu(BigInteger) | 0 | 1 | 1 | 1 |
Class | OCavg | OCmax | WMC | |
Dan | 2.33 | 5 | 21 | |
Expr | 4.33 | 14 | 26 | |
Ju | 5.75 | 17 | 23 | |
MainClass | 1 | 1 | 1 | |
Term | 3.6 | 14 | 18 | |
Var | 1 | 1 | 5 |
度量分析:观察以上数据可知,本实验中Ju(句法分析)与Expr(表达式)以及Term的类方法平均循环复杂度均较高,parseFactor 方法与getVar方法的非结构化程度均不理想,这也确实是我代码出错的原因,而这两段关键代码被函数反复调用,表示出某些地方耦 合度过高,这使我的程序受到了极大的影响。
bug分析:本次实验我最终并没有通过中测的进阶测试,在强测中更是出现了7个bug,经过我个人的查找,发现parseFactor方法 中,解析表达式因子完成后,我直接步进,而返回parseTerm时再次步进,导致“*”读取的失败,使得项中因子缺失。
优缺点点评:本次实验我的代码具有的优点可能就是方法极度白痴,简单分为若干类,对于类的属性以及功用分析简单明了。
缺点在于合并同类项等处存在大量代码复用问题,这个问题可以通过提炼为方法而解决,同时步进(gonext)方法过于单一,使用 时容易出现问题,程序鲁棒性差。
-
Homework 2 分析
总述:如下UML类图,第二次作业是在第一次作业基础上迭代,表达式中支持自定义函数,求和函数,同时简单变量中引入了三角函数.变量最终化简形式为"a*x**b*sin()**c*cos()**d"其中sin与cos中内容为常数因子以及x的幂次。据此,我们仍然是使用hashmap来构造,但是此次b到最终式并非单一映射,故考虑将“b*sin()**c*cos()**d”作为索引的key,那么相应的,我们需要储存sin与cos的内部内容以及各自的指数,若使用两字符串两数字来表征处理较为困难,故考虑将“sin()**c*cos()**d”作为字符串信息处理,为Var(简单变量)新增tan(三角相关的字符串),建立索引Vardir。
在总结上次失败的教训后,我拆分了计算时用到的加法与乘法方法(其实可以考虑对加法和乘法单独建类),即mult与add方法,而在解决简单变量相乘问题时,引入三角函数字符串处理方法,此处使用了正则表达式拆分并建立对应的sanjiaopipei的对象。简单但是为日后迭代带来不便。同时在句法分析(ju)中拆分了遇到表达式因子与简单因子时的不同方法,设立了使用正则分析的parseSincos方法。
同时,由于自定义函数表达式也需要进行预处理,故将Mainclass中的预处理单独拆分为pre方法,为了解决“+--++-”等过度出现的问题,笔者使用正则表达式捕获组循环化简。而总结函数的若干属性,我们可以发现有函数名,形参列表与实参列表,函数表达式以及函数最终代换结果。在表达式中读取到函数时,我们先跳转到replacefun方法中,随后链接到相应函数(f,g,h或者sum)的替换方式,此处仍然使用正则表达式,因为本次函数的实参较为简单,故运行中没有出现错误。
Method CogC ev(G) iv(G) v(G) Dan.getDuan() 0 1 1 1 Dan.getNumber(int) 8 1 9 9 Dan.getPos() 0 1 1 1 Dan.getVar() 34 6 15 18 Dan.gonext() 4 2 7 7 Dan.numpos(int) 5 1 5 7 Dan.setDuan(String) 0 1 1 1 Dan.setInput(String) 0 1 1 1 Dan.setPos(int) 0 1 1 1 Expr.addTerm(Term) 0 1 1 1 Expr.calFactor() 20 2 7 8 Expr.easycal() 15 1 7 7 Expr.iniExpr() 0 1 1 1 Expr.printExpr() 22 1 8 9 Expr.setPower(int) 0 1 1 1 Expr.setTerms(HashSet<Term>) 0 1 1 1 Function.funexp(HashMap<String, Function>) 0 1 1 1 Function.getExpr() 0 1 1 1 Function.getName() 0 1 1 1 Function.parse(String) 4 1 4 4 Function.replace(String) 3 1 4 4 Function.sumreplace(String) 10 3 5 6 Ju.classify() 16 9 13 14 Ju.exprFactor() 5 1 4 4 Ju.fuhao() 0 1 1 1 Ju.parseExpr() 2 1 3 3 Ju.parseFactor() 2 2 2 2 Ju.parseSincos(String) 0 1 1 1 Ju.parseTerm(boolean) 1 1 2 2 Ju.setLittle(Dan) 0 1 1 1 Ju.sincos(BigInteger) 5 1 4 4 MainClass.main(String[]) 2 1 3 3 MainClass.pre(String) 3 1 4 4 MainClass.replacefun(String, HashMap<String, Function>) 40 6 17 18 Term.addFactor(Factor) 0 1 1 1 Term.cal() 11 4 4 5 Term.iniTerm() 0 1 1 1 Term.ml(HashSet<Var>, HashSet<Var>) 14 1 7 7 Term.setNeg(boolean) 0 1 1 1 Term.subcal() 3 1 3 3 Var.calFactor() 0 1 1 1 Var.getPower() 0 1 1 1 Var.getTan() 0 1 1 1 Var.getXishu() 0 1 1 1 Var.mult(Var) 12 1 8 8 Var.setPower(int) 0 1 1 1 Var.setTan(String) 0 1 1 1 Var.setVar(BigInteger, int, String) 0 1 1 1 Var.setXishu(BigInteger) 0 1 1 1 Vardir.add(Vardir) 12 1 8 8 Vardir.equals(Object) 4 3 3 5 Vardir.getPower() 0 1 1 1 Vardir.getTan() 0 1 1 1 Vardir.hashCode() 0 1 1 1 Vardir.setPower(int) 0 1 1 1 Vardir.setTan(String) 0 1 1 1 sanjiaopipei.equals(Object) 4 3 3 5 sanjiaopipei.getList() 0 1 1 1 sanjiaopipei.getName() 0 1 1 1 sanjiaopipei.hashCode() 0 1 1 1 sanjiaopipei.set(String, String) 0 1 1 1 sanjiaopipei.setList(String) 0 1 1 1 sanjiaopipei.setName(String) 0 1 1 1 Class OCavg OCmax WMC Dan 3.22 13 29 Expr 4.62 9 37 Function 2.83 6 17 Ju 3.33 12 30 MainClass 6.33 12 19 Term 2.83 6 17 Var 2.3 8 23 Vardir 2.29 8 16 sanjiaopipei 1.29 3 9 度量分析:可以看到本次构造中MainClass的平均循环复杂度得到了很大的提升,这和pre方法的改变有较大的关联。由于我有意识的拆分,各类的类方法数量也有显著的提升。而方法中,词法分析中的getVar被大量关联,Expr中的calFactor以及printExpr以及MainClass中的函数代换,均存在非结构程度高,耦合复杂以及判断路径复杂的问题。这三个函数确实内部较为复杂,难以维护。
bug分析:本次我仍然是很遗憾地没有通过中测,相比于上次错误两个点,这次仅有一处错误,课下发现原来是函数类在进行代换时,每次实参列表仅仅是增加新的实参进来,再从头开始计数匹配,如计算f(x)+f(x**2)时实际计算的是f(x)+f(x),没发现这个bug让我明白自己造数据能力较差,并且对指导书要求理解不深。
优缺点点评:本次代码的优点在于相比于第一次的设计,方法拆分做的不错,也有了更加明确的完成目标,中间经历了三角函数相关内容的重构,也让我意识到对类属性认知的重要性。本次运用了正则表达式,便于分析,但是不支持嵌套,因而难以在此基础上实现迭代。
-
Homework 3 分析
总述:如下UML类图,第三次作业相比于第二次作业增加的要求便是:支持多项式函数以及三角函数的嵌套,第二次使用正则表达式偷懒的我狠狠地吃了一瘪,不过这个问题还算好解决。将replacefu方法设置为递归调用,每一次递归都替换表达式中当前层次的函数因子。最后便可以展开为无函数表达式。而在sin,cos,sum等的匹配过程中,我将使用正则表达式的方法替换为了使用括号匹配判别的方法,保证了准确性。针对本次要求sin与cos中必须为因子,我又使用了halfprintf方法,完成了内部解析判别。就这样我顺利通过了第三次作业的中测,进入到了互测环节。
Method | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
Dan.getDuan() | 0 | 1 | 1 | 1 |
Dan.getNumber(int) | 8 | 1 | 9 | 9 |
Dan.getPos() | 0 | 1 | 1 | 1 |
Dan.getVar() | 34 | 6 | 15 | 18 |
Dan.gonext() | 4 | 2 | 7 | 7 |
Dan.numpos(int) | 5 | 1 | 5 | 7 |
Dan.setDuan(String) | 0 | 1 | 1 | 1 |
Dan.setInput(String) | 0 | 1 | 1 | 1 |
Dan.setPos(int) | 0 | 1 | 1 | 1 |
Expr.addTerm(Term) | 0 | 1 | 1 | 1 |
Expr.calFactor() | 20 | 2 | 7 | 8 |
Expr.easycal() | 15 | 1 | 7 | 7 |
Expr.halfprintExpr() | 35 | 1 | 13 | 14 |
Expr.iniExpr() | 0 | 1 | 1 | 1 |
Expr.printExpr() | 35 | 1 | 13 | 14 |
Expr.setPower(int) | 0 | 1 | 1 | 1 |
Expr.setTerms(HashSet<Term>) | 0 | 1 | 1 | 1 |
Function.funexp(HashMap<String, Function>) | 0 | 1 | 1 | 1 |
Function.getExpr() | 0 | 1 | 1 | 1 |
Function.getName() | 0 | 1 | 1 | 1 |
Function.parse(String) | 4 | 1 | 4 | 4 |
Function.replace(String) | 24 | 1 | 8 | 10 |
Function.sumreplace(String) | 26 | 5 | 9 | 12 |
Ju.classify() | 16 | 9 | 13 | 14 |
Ju.exprFactor() | 5 | 1 | 4 | 4 |
Ju.fuhao() | 0 | 1 | 1 | 1 |
Ju.inout(String) | 12 | 5 | 5 | 9 |
Ju.parseExpr() | 2 | 1 | 3 | 3 |
Ju.parseFactor() | 2 | 2 | 2 | 2 |
Ju.parseSincos(String) | 0 | 1 | 1 | 1 |
Ju.parseTerm(boolean) | 1 | 1 | 2 | 2 |
Ju.setLittle(Dan) | 0 | 1 | 1 | 1 |
Ju.sincos(BigInteger) | 8 | 1 | 6 | 6 |
MainClass.main(String[]) | 1 | 1 | 2 | 2 |
MainClass.pre(String) | 3 | 1 | 4 | 4 |
MainClass.replacefun(String, HashMap<String, Function>) | 40 | 1 | 17 | 18 |
Term.addFactor(Factor) | 0 | 1 | 1 | 1 |
Term.cal() | 11 | 4 | 4 | 5 |
Term.iniTerm() | 0 | 1 | 1 | 1 |
Term.ml(HashSet<Var>, HashSet<Var>) | 14 | 1 | 7 | 7 |
Term.setNeg(boolean) | 0 | 1 | 1 | 1 |
Term.subcal() | 3 | 1 | 3 | 3 |
Var.calFactor() | 0 | 1 | 1 | 1 |
Var.getPower() | 0 | 1 | 1 | 1 |
Var.getTan() | 0 | 1 | 1 | 1 |
Var.getXishu() | 0 | 1 | 1 | 1 |
Var.halfprintExpr() | 22 | 2 | 14 | 14 |
Var.mult(Var) | 44 | 1 | 19 | 21 |
Var.setPower(int) | 0 | 1 | 1 | 1 |
Var.setTan(String) | 0 | 1 | 1 | 1 |
Var.setVar(BigInteger, int, String) | 0 | 1 | 1 | 1 |
Var.setXishu(BigInteger) | 0 | 1 | 1 | 1 |
Vardir.add(Vardir) | 42 | 1 | 17 | 19 |
Vardir.equals(Object) | 4 | 3 | 3 | 5 |
Vardir.getPower() | 0 | 1 | 1 | 1 |
Vardir.getTan() | 0 | 1 | 1 | 1 |
Vardir.hashCode() | 0 | 1 | 1 | 1 |
Vardir.setPower(int) | 0 | 1 | 1 | 1 |
Vardir.setTan(String) | 0 | 1 | 1 | 1 |
sanjiaopipei.equals(Object) | 4 | 3 | 3 | 5 |
sanjiaopipei.getList() | 0 | 1 | 1 | 1 |
sanjiaopipei.getName() | 0 | 1 | 1 | 1 |
sanjiaopipei.hashCode() | 0 | 1 | 1 | 1 |
sanjiaopipei.set(String, String) | 0 | 1 | 1 | 1 |
sanjiaopipei.setList(String) | 0 | 1 | 1 | 1 |
sanjiaopipei.setName(String) | 0 | 1 | 1 | 1 |
Class | OCavg | OCmax | WMC | |
Dan | 3.22 | 13 | 29 | |
Expr | 5.62 | 13 | 45 | |
Function | 4.83 | 12 | 29 | |
Ju | 3.9 | 12 | 39 | |
MainClass | 6 | 12 | 18 | |
Term | 2.83 | 6 | 17 | |
Var | 3.5 | 17 | 35 | |
Vardir | 3.57 | 17 | 25 | |
sanjiaopipei | 1.29 | 3 | 9 |
度量分析:可以看到本次构造中由于维护sin以及cos中内容为因子的halfprint方法的引入以及使用括号匹配带来的循环嵌套,Expr的类循环复杂度有所提升,递归也让Function类的循环复杂度上升。而本次由于halfprintf方法的引入,mult与add方法的耦合程度变得复杂,由于递归的存在,replace的耦合程度也大幅度增加。
bug分析与hack策略:本次程序的主要bug在于,输入的数字变量可能爆int,如sum中的上下限,对此,我使用long类型转化字符串即可。本次我hack时采用的策略是多层嵌套三角如sin(cos())+cos(sin(cos))同时内部设置过量+号,hack了两位同学。
优缺点点评:本次代码的优点在于相比于第二次的设计,使用了括号匹配代替正则,起到了很好的作用,缺点在于没有很好地提取括号匹配的公共方法,导致代码量较大。
第一单元总结语