面向对象程序设计——表达式求导问题总结

整体架构

 

  第一次实验中问题比较简单,我的设计架构十分面向过程,只有两个类这里就没脸说了,平均的方法复杂度也是蛮高的。

methodev(G)iv(G)v(G)
Expression.addExp(BigInteger,BigInteger)1.02.02.0
Expression.Expression(String)7.06.09.0
Expression.getCoeff(String)5.08.08.0
Expression.getDifferential()4.014.015.0
Expression.getExpSet()1.01.01.0
Expression.getMultiply(String)2.02.02.0
Expression.getPower(String)2.03.03.0
Run.main(String[])1.02.02.0
Total23.038.042.0
Average2.8754.755.25


第一次作业复杂度分析

  到了第二次我才分出了Expression, Term, 和Factor类,Factor之间相乘形成Term,Term之间相加形成Expession。但这次我并没有运用接口或抽象类,还是没有很面向对象。方法的平均复杂度下降了一些,但也有一些方法的复杂度仍然较高。

第二次作业类图

Term.toString()4.07.08.0
Term.Term(String)2.05.08.0
Term.Term(Factor,Factor,Factor,BigInteger)1.01.01.0
Term.Term()1.01.01.0
Term.simplify(Term)4.010.011.0
Term.multiply(Term)2.01.02.0
Term.hashCode()1.01.01.0
Term.getRemain(FactorType)5.02.05.0
Term.getDifferential()1.04.04.0
Term.getCoefficient()1.01.01.0
Term.equals(Object)1.01.01.0
Run.main(String[])1.02.02.0
FormatException.FormatException()1.01.01.0
Factor.toString()11.08.011.0
Factor.multiplySameType(Factor)2.02.02.0
Factor.getPower()1.01.01.0
Factor.getFactorType()1.01.01.0
Factor.getDifferential()6.05.06.0
Factor.Factor(String)4.04.04.0
Factor.Factor(FactorType,BigInteger)1.01.01.0
Factor.equals(Object)1.01.01.0
Factor.construct(String)1.01.02.0
Expression.toString()4.06.07.0
Expression.simplify()4.04.04.0
Expression.replaceBack(String)1.01.01.0
Expression.replace(String)1.01.01.0
Expression.getDifferential()1.03.03.0
Expression.Expression(String)7.03.08.0
Expression.Expression(ArrayList)1.02.02.0
Expression.Expression()1.01.01.0
Expression.combine(Expression)1.03.03.0
Expression.cleanSign(String,int)4.05.06.0
Expression.checkSpaceError(String)2.01.02.0
Expression.checkSign(String)2.02.03.0
Expression.addExp(Term)2.02.03.0
Total85.096.0120.0
Average2.3611111111111112.66666666666666653.3333333333333335

  第二次作业复杂度分析

  第三次作业中我对Factor类使用了抽象类,并由四个子类NumFactor, SinCosFactor, PowerFactor, ExpFactor分别实现Factor类中声明的方法。但还是没有对Expression,Term, Factor这几个顶层类运用接口,也未将字符串处理单独分离出来形成类,而是嵌入了Expression,Term,Factor的构造方法之中,颇显臃肿。类与类之间的耦合也较严重,类图画出来颇显杂乱。但方法的平均复杂度进一步降低,个别复杂度高的方法的数量也减少了很多。                       

                                                                                                                 

第三次作业类图

 methodev(G) iv(G) v(G)
Construct.constructFactor(String)6.06.06.0
Construct.outerBracketMatch(String)5.06.06.0
ExpFactor.combine(Factor)1.01.01.0
ExpFactor.equals(Object)1.01.01.0
ExpFactor.ExpFactor(Expression)1.01.01.0
ExpFactor.ExpFactor(String)1.01.01.0
ExpFactor.getDifferential()1.01.01.0
ExpFactor.toString()2.02.02.0
Expression.checkSign(String)2.02.03.0
Expression.checkSpaceError(String)2.01.02.0
Expression.cleanSign(String,int)4.05.06.0
Expression.combine(Expression)1.01.01.0
Expression.combineTerm()5.05.05.0
Expression.Expression()1.01.01.0
Expression.Expression(ArrayList)1.01.01.0
Expression.Expression(String)5.06.08.0
Expression.getDifferential()1.02.02.0
Expression.handleMultiSigns(String)4.01.04.0
Expression.onlyOneFactor()1.01.01.0
Expression.replace(String)1.01.01.0
Expression.replaceBack(String)1.01.01.0
Expression.simplify()1.02.02.0
Expression.toString()4.03.04.0
Factor.Factor()1.01.01.0
FormatException.FormatException()1.01.01.0
NumFactor.combine(Factor)2.02.02.0
NumFactor.equals(Object)2.02.02.0
NumFactor.getDifferential()1.01.01.0
NumFactor.getNumber()1.01.01.0
NumFactor.NumFactor(BigInteger)1.01.01.0
NumFactor.NumFactor(String)1.01.01.0
NumFactor.toString()1.01.01.0
PowerFactor.combine(Factor)2.02.02.0
PowerFactor.equals(Object)2.02.02.0
PowerFactor.getDifferential()1.01.01.0
PowerFactor.getPower()1.01.01.0
PowerFactor.PowerFactor(BigInteger)1.01.01.0
PowerFactor.PowerFactor(String)3.02.03.0
PowerFactor.toString()1.02.02.0
Run.main(String[])1.02.02.0
SincosFactor.combine(Factor)3.03.03.0
SincosFactor.equals(Object)2.02.02.0
SincosFactor.getDiffCoeff()2.02.02.0
SincosFactor.getDifferential()1.01.01.0
SincosFactor.getDiffType()2.01.02.0
SincosFactor.getPower()1.01.01.0
SincosFactor.SincosFactor(String,String,Type)3.02.03.0
SincosFactor.SincosFactor(Type,Factor,BigInteger)1.01.01.0
SincosFactor.toString()1.02.03.0
Term.canCombine(Term)3.03.03.0
Term.checkCombine()5.05.05.0
Term.checkZero()3.010.010.0
Term.combine(Term)1.01.01.0
Term.getDifferential()1.02.02.0
Term.isEmpty()1.01.01.0
Term.onlyOneFactor()1.01.01.0
Term.simplify()2.01.02.0
Term.Term(ArrayList)1.01.01.0
Term.Term(String)2.05.05.0
Term.toString()1.02.02.0
Total112.0123.0137.0
Average1.86666666666666672.052.283333333333333

                                                                                                                               第三次作业复杂度分析
  听了课上同学们的分享,再和自己的架构进行对比后,深感自己对面向对象理解还很不到位,很多思想仍然很面向对象,决心好好学习QAQ!  

 


 

输入处理思路

  这次的表达式求导实验中,我认为最难的点在于对输入字符串的识别和处理,在第一次作业中由于输入的格式较为固定,所以我使用了正则表达式+类似于状态机的识别方法,从头到尾依次根据输入是否有符号有几个符号,是否有数字,是否有x项及是否有次方等,对当前状态进行转移并处理(但我写的时候并没有认识到这是一个状态机,所以直接用if和else嵌套一大堆来实现了orz)。

  但这个方法到了第二次作业就失效了,因为第二次作业不仅加入了sin(x),cos(x)因子,还没有了对因子位置和数量的限制。所以我就转变了思路,开始使用在第一次实验中我本身不太认可的方法——将整个表达式用+/-分割为若干项,再将每一项用*分割为若干因子。在分割之前,需要对是否有非法字符、是否有非法空格位置,以及连续出现的+/-号是否合法等问题进行处理,且在利用+/-进行分割时,由于表达式中不仅在项和项连接处会出现+/-,在项内也会出现,但都与*或^相连,所以我将这些项内的 ^+/^-/*+/*- 用其他字符进行了替换处理再进行分割(在分割后再换回)。用*分割项时直接分割即可。另外,在将表达式分割时,默认分割的各项是合法的,在分割项时也默认各因子是合法的,其具体合法性在分割结束后传入下层结构(Term/Factor)进行识别时再分别判断。

 1     //蠢之又蠢的替换QAQ
 2     private String replace(String str) {
 3         return str.replaceAll("\\^\\+", "@").replaceAll("\\^-", "#")
 4                 .replaceAll("\\*\\+", "&").replaceAll("\\*-", "%");
 5     }
 6 
 7     private String replaceBack(String str) {
 8         return str.replaceAll("@", "^+").replaceAll("#", "^-")
 9                 .replaceAll("&", "*+").replaceAll("%", "*-");
10     }
View Code

  在第三次作业中,如何将第二次作业的方法继续应用又使我伤了脑筋,我本着能修改就不重构的原则(误)最终还是将第二次的方法完美使用在了第三次作业中。

  第三次作业的特点是加入了因子和表达式的嵌套,但我们发现,所有嵌套的发生都伴随着两个特征符号即为”(“ ”)“的出现,如果无视括号内的内容,将括号作为一个整体来看,就可以完全地使用第二次作业中的办法进行处理。怎么使程序将括号整个当做整体看待呢?我能想到的就只有替换了,既然正则表达式无法很好地完成括号匹配的任务,那么我就请出了括号匹配界的专家——栈,使用栈对表达式的括号进行匹配检查,并找到当前字符串内“最外侧”的括号,将这些括号内的字符串内容用一个识别串(含编号)进行替换,并将其内容存入Arraylist中。这样就可以套用第二次的方法无视括号内的内容进行分割,在分割后再将之前替换掉的内容根据编号替换回来即可。最外层括号识别替换在处理Expression和Term的时候均有使用,至于多层括号的情况,就交给Expression,Term,Factor之间的嵌套来自动处理啦!

 1 //匹配最外层括号,返回他们的位置
 2 public TreeMap<Integer, Integer> outerBracketMatch(String str) {
 3         Stack<Integer> stack = new Stack<>();
 4         TreeMap<Integer, Integer> brackets = new TreeMap<>();
 5         for (int i = 0; i < str.length(); i++) {
 6             if (str.charAt(i) == '(') {
 7                 stack.push(i);
 8             } else if (str.charAt(i) == ')') {
 9                 if (stack.empty()) {
10                     return null;
11                 } else {
12                     int t = stack.pop();
13                     if (stack.empty()) {
14                         brackets.put(t, i);
15                     }
16                 }
17             }
18         }
19         return brackets;
20     }
View Code

  以上就是我的输入处理方法,写出来真的蛮显冗长,也不推荐大家使用,但还是希望能给大家带来一些想法吧。看了大佬的递归下降方法之后,我就觉得我的方法实在是难以入目了,滚去膜大佬了。

 


 

BUG

  本次实验中比较幸运的是,在公测和互测中一共只在第二次作业的互测中被找出一个bug(但是被残忍地刀了二十多次),这个bug是由于我在解析表达式,对表达式进行分割时使用了String.split(“*”),但是忽略了这个方法在分割时会自动忽略头尾*的特点(我以为如果头尾有*的话,会多分出来一个空字符串,然而并不会),解决方法是加一个头尾*的检测判断,虽然有效,但这样使得整个程序颇有拆拆补补的感觉,这也是我的整体设计上不好的地方,十分容易出现这种小的问题需要分别考虑并解决。

   在检查他人bug时,我基本使用的是经验数据来进行测试。我发现大家除了在第一次作业中存在爆栈的情况外,之后的作业中大多不会在正常或者较长的数据中产生bug,相反的是容易在一些较短的特殊数据中产生bug,例如:

*
++
123
1 x
1*

  等仅有一个或数个符号,或仅有一个常数,或多了少了一个符号的地方发生错误。这说明了大家在考虑复杂问题的时候,忽略掉了简单的边界问题。


Applying Creational Pattern

  (这一节是要说自己的改进想法吗?)

   在我的实验中,我没能充分利用面向对象设计构造中接口的特性,所以最重要的,我会在Expression, Term, Factor上定义一个Computerable的接口,其中声明getDifferential,simplify,combine等方法,由Expression,Term,Factor等类和接口进行继承和实现;并且将字符串处理单独建立ExpParser, TermParser, FactorParser等类和接口进行处理,并且尝试使用面向对象的递归下降算法对输入进行处理和解析。

转载于:https://www.cnblogs.com/yhhhz/p/10588214.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值