301
社区成员
发帖
与我相关
我的任务
分享
架构主要分为四个部分
Term,Factor一同构成表达式的数据结构Mapping和其管理类MapSet管理自定义函数Parser,Lexer负责解析输入Tools,IoManager,程序入口Main表达式由Term和Factor组成,按照递归下降的解析顺序储存为树状结构,其中Expr类既可以作为Term的上级也可以作为因子(和递归下降的解析顺序相同)
表达式的化简由函数simplify()负责,每一个类中都实现了相应的simplify()方法,这个方法会返回一个Expr类,类的内容为该因子/项化简后储存在一个表达式结构的结果,通过递归地调用这个方法就可以实现对表达式的化简,以下为Exp实现的例子
@Override
public Expr simplify() {
Expr ret = new Expr();
Term term = new Term(1);
term.setExp(copy());
ret.getTerms().add(term);
return ret;
}
调用toString()方法生成类的字符串表示,这个过程同样是递归的
实现
toString()方法在debug的过程中也很有意义
调用derive()可以获得该因子/项的导数,结果同样储存在Expr中
通过传入mapping类(其中包含了自定义函数相关的参数),可以将因子中包含的函数代换为实际的表达式
深复制
通过实现以上几个方法,就可以扩展Factor,实现新的因子
为了管理自定义函数,我设计了管理单个函数映射的Mapping和管理多个mapping的MapSet
读取自定义函数时,会创建一个新的Mapping对象储存函数名,函数变量和函数表达式,表达式同样以递归下降的方式解析并储存,为了区分自定义函数中的变量和最终表达式中的变量,mapping中储存的函数变量名前会加上$
所有mapping对象由MapSet对象管理,通过调用mapSet.map(Expr),可以获取传入表达式对mapSet中所有mapping映射后的表达式
管理输入解析,没有太多值得讨论的细节,主要采用递归下降的思想解析输入
作为工具类实现静态的工具函数
整个架构充分考虑到了增量开发和扩展的需求,实际上在添加功能的时候效率尚可。然而在实现的过程中,由于因子和项之间复杂的包含关系导致类与类之间关系冗杂,难以维护(主要还是编写代码时不够认真,具体的实现没有做到足够精简),开发过程中经历了几次小规模的重构
Factor的数据结构从ArrayList<Factor>转为对每种Factor分别提供存储equals方法,提供因子与因子、项与项之间比较的功能,用于化简对比其他同学分享的架构,我并没有单独实现多项式与单项式的类用于表示化简结果,因为事实上化简结果本身就是一个表达式,利用表达式进行储存绰绰有余
| Complexity metrics | Wed | 20 Mar 2024 11:10:22 CST | ||
|---|---|---|---|---|
| Method | CogC | ev(G) | iv(G) | v(G) |
| Main.main(String[]) | 1 | 1 | 2 | 2 |
| expression.Deri.Deri(Expr) | 0 | 1 | 1 | 1 |
| expression.Deri.assign(MapSet) | 0 | 1 | 1 | 1 |
| expression.Deri.combine(Factor) | 0 | 1 | 1 | 1 |
| expression.Deri.copy() | 0 | 1 | 1 | 1 |
| expression.Deri.derive() | 0 | 1 | 1 | 1 |
| expression.Deri.equals(Object) | 2 | 3 | 1 | 3 |
| expression.Deri.getExpr() | 0 | 1 | 1 | 1 |
| expression.Deri.getType() | 0 | 1 | 1 | 1 |
| expression.Deri.getValue() | 0 | 1 | 1 | 1 |
| expression.Deri.map(Mapping) | 0 | 1 | 1 | 1 |
| expression.Deri.mulDeris(ArrayList, ArrayList) | 2 | 1 | 3 | 3 |
| expression.Deri.setExpr(Expr) | 0 | 1 | 1 | 1 |
| expression.Deri.setValue(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Deri.simplify() | 0 | 1 | 1 | 1 |
| expression.Deri.toString() | 0 | 1 | 1 | 1 |
| expression.Deri.varify(ArrayList) | 0 | 1 | 1 | 1 |
| expression.Exp.Exp() | 0 | 1 | 1 | 1 |
| expression.Exp.Exp(Expr) | 0 | 1 | 1 | 1 |
| expression.Exp.Exp(Expr, BigInteger) | 0 | 1 | 1 | 1 |
| expression.Exp.assign(MapSet) | 0 | 1 | 1 | 1 |
| expression.Exp.combine(Exp, Exp) | 0 | 1 | 1 | 1 |
| expression.Exp.combine(Factor) | 0 | 1 | 1 | 1 |
| expression.Exp.copy() | 0 | 1 | 1 | 1 |
| expression.Exp.derive() | 0 | 1 | 1 | 1 |
| expression.Exp.equals(Object) | 3 | 4 | 1 | 4 |
| expression.Exp.getExpr() | 0 | 1 | 1 | 1 |
| expression.Exp.getType() | 0 | 1 | 1 | 1 |
| expression.Exp.getValue() | 0 | 1 | 1 | 1 |
| expression.Exp.isEmpty() | 0 | 1 | 1 | 1 |
| expression.Exp.map(Mapping) | 0 | 1 | 1 | 1 |
| expression.Exp.mul(Exp, Exp) | 3 | 3 | 4 | 4 |
| expression.Exp.setExpr(Expr) | 0 | 1 | 1 | 1 |
| expression.Exp.setValue(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Exp.simplify() | 0 | 1 | 1 | 1 |
| expression.Exp.toString() | 0 | 1 | 1 | 1 |
| expression.Exp.varify(ArrayList) | 0 | 1 | 1 | 1 |
| expression.Expr.Expr() | 0 | 1 | 1 | 1 |
| expression.Expr.Expr(ArrayList) | 0 | 1 | 1 | 1 |
| expression.Expr.addExpr(Expr, Expr) | 1 | 1 | 2 | 2 |
| expression.Expr.addTerm(Term) | 3 | 3 | 2 | 3 |
| expression.Expr.assign(MapSet) | 1 | 1 | 2 | 2 |
| expression.Expr.combine(Factor) | 0 | 1 | 1 | 1 |
| expression.Expr.copy() | 1 | 1 | 2 | 2 |
| expression.Expr.derive() | 1 | 1 | 2 | 2 |
| expression.Expr.equals(Object) | 10 | 8 | 3 | 8 |
| expression.Expr.getTerms() | 0 | 1 | 1 | 1 |
| expression.Expr.getType() | 0 | 1 | 1 | 1 |
| expression.Expr.getValue() | 0 | 1 | 1 | 1 |
| expression.Expr.isEmpty() | 0 | 1 | 1 | 1 |
| expression.Expr.map(Mapping) | 1 | 1 | 2 | 2 |
| expression.Expr.mulExpr(Expr, Expr) | 7 | 1 | 4 | 4 |
| expression.Expr.setValue(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Expr.simplify() | 5 | 1 | 4 | 4 |
| expression.Expr.toString() | 11 | 5 | 8 | 10 |
| expression.Expr.varify(ArrayList) | 1 | 1 | 2 | 2 |
| expression.Functor.Functor(String, ArrayList, BigInteger) | 0 | 1 | 1 | 1 |
| expression.Functor.combine(Factor) | 10 | 6 | 3 | 6 |
| expression.Functor.copy() | 1 | 1 | 2 | 2 |
| expression.Functor.derive() | 1 | 1 | 2 | 2 |
| expression.Functor.equals(Object) | 8 | 8 | 2 | 8 |
| expression.Functor.getName() | 0 | 1 | 1 | 1 |
| expression.Functor.getType() | 0 | 1 | 1 | 1 |
| expression.Functor.getValue() | 0 | 1 | 1 | 1 |
| expression.Functor.getVar(int) | 0 | 1 | 1 | 1 |
| expression.Functor.getVars() | 0 | 1 | 1 | 1 |
| expression.Functor.map(Mapping) | 0 | 1 | 1 | 1 |
| expression.Functor.mulFunctors(ArrayList, ArrayList) | 21 | 5 | 8 | 9 |
| expression.Functor.setValue(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Functor.simplify() | 0 | 1 | 1 | 1 |
| expression.Functor.toString() | 1 | 1 | 2 | 2 |
| expression.Number.Number(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Number.combine(Factor) | 1 | 2 | 2 | 2 |
| expression.Number.copy() | 0 | 1 | 1 | 1 |
| expression.Number.equals(Object) | 2 | 3 | 1 | 3 |
| expression.Number.getType() | 0 | 1 | 1 | 1 |
| expression.Number.getValue() | 0 | 1 | 1 | 1 |
| expression.Number.setValue(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Number.simplify() | 1 | 1 | 1 | 2 |
| expression.Number.toString() | 0 | 1 | 1 | 1 |
| expression.Power.Power(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Power.Power(BigInteger, String) | 0 | 1 | 1 | 1 |
| expression.Power.combine(Factor) | 3 | 3 | 3 | 3 |
| expression.Power.copy() | 0 | 1 | 1 | 1 |
| expression.Power.derive() | 0 | 1 | 1 | 1 |
| expression.Power.equals(Object) | 4 | 5 | 1 | 5 |
| expression.Power.getType() | 0 | 1 | 1 | 1 |
| expression.Power.getValue() | 0 | 1 | 1 | 1 |
| expression.Power.getVarName() | 0 | 1 | 1 | 1 |
| expression.Power.mulPowers(HashMap<String, Power>, HashMap<String, Power>) | 21 | 5 | 9 | 9 |
| expression.Power.setValue(BigInteger) | 0 | 1 | 1 | 1 |
| expression.Power.setVarName(String) | 0 | 1 | 1 | 1 |
| expression.Power.simplify() | 0 | 1 | 1 | 1 |
| expression.Power.toString() | 3 | 4 | 1 | 4 |
| expression.Term.Term(int) | 0 | 1 | 1 | 1 |
| expression.Term.addFactor(Factor) | 9 | 3 | 3 | 10 |
| expression.Term.assign(MapSet) | 3 | 1 | 3 | 3 |
| expression.Term.combine(Term) | 2 | 2 | 2 | 3 |
| expression.Term.combineTerms(Expr) | 18 | 5 | 7 | 7 |
| expression.Term.copy() | 4 | 1 | 5 | 5 |
| expression.Term.derive() | 9 | 1 | 6 | 6 |
| expression.Term.equals(Object) | 41 | 23 | 10 | 23 |
| expression.Term.getDeris() | 0 | 1 | 1 | 1 |
| expression.Term.getExp() | 0 | 1 | 1 | 1 |
| expression.Term.getExprs() | 0 | 1 | 1 | 1 |
| expression.Term.getFunctors() | 0 | 1 | 1 | 1 |
| expression.Term.getNumber() | 0 | 1 | 1 | 1 |
| expression.Term.getPositivity() | 0 | 1 | 1 | 1 |
| expression.Term.getPowers() | 0 | 1 | 1 | 1 |
| expression.Term.isCombinable(Term) | 19 | 12 | 7 | 15 |
| expression.Term.map(Mapping) | 3 | 1 | 3 | 3 |
| expression.Term.mul(Term) | 2 | 2 | 2 | 3 |
| expression.Term.setDeris(ArrayList) | 0 | 1 | 1 | 1 |
| expression.Term.setExp(Exp) | 0 | 1 | 1 | 1 |
| expression.Term.setFunctors(ArrayList) | 0 | 1 | 1 | 1 |
| expression.Term.setNumber(Number) | 0 | 1 | 1 | 1 |
| expression.Term.setPowers(HashMap<String, Power>) | 0 | 1 | 1 | 1 |
| expression.Term.simple() | 0 | 1 | 1 | 1 |
| expression.Term.simplify() | 8 | 1 | 7 | 7 |
| expression.Term.toString() | 18 | 4 | 13 | 15 |
| expression.Term.valueEquals(Term, Term) | 0 | 1 | 1 | 1 |
| expression.Term.varify(ArrayList) | 3 | 1 | 3 | 3 |
| func.MapSet.MapSet() | 0 | 1 | 1 | 1 |
| func.MapSet.add(Mapping) | 0 | 1 | 1 | 1 |
| func.MapSet.assign(Deri) | 0 | 1 | 1 | 1 |
| func.MapSet.assign(Expr) | 0 | 1 | 1 | 1 |
| func.MapSet.map(Functor) | 1 | 2 | 2 | 2 |
| func.MapSet.mapNames() | 0 | 1 | 1 | 1 |
| func.Mapping.Mapping(String, ArrayList, Expr) | 0 | 1 | 1 | 1 |
| func.Mapping.assign(MapSet) | 0 | 1 | 1 | 1 |
| func.Mapping.getName() | 0 | 1 | 1 | 1 |
| func.Mapping.getVar(int) | 0 | 1 | 1 | 1 |
| func.Mapping.getVarByName(String) | 3 | 3 | 2 | 3 |
| func.Mapping.getVars() | 0 | 1 | 1 | 1 |
| func.Mapping.map(Functor) | 1 | 2 | 1 | 2 |
| func.Mapping.target(String) | 0 | 1 | 1 | 1 |
| func.Mapping.varify() | 0 | 1 | 1 | 1 |
| util.IoManager.IoManager() | 0 | 1 | 1 | 1 |
| util.IoManager.getInt() | 0 | 1 | 1 | 1 |
| util.IoManager.getLine() | 0 | 1 | 1 | 1 |
| util.Lexer.Lexer(String) | 0 | 1 | 1 | 1 |
| util.Lexer.getNumber() | 4 | 1 | 4 | 4 |
| util.Lexer.getVar() | 4 | 1 | 4 | 4 |
| util.Lexer.next() | 4 | 2 | 3 | 4 |
| util.Lexer.peek() | 0 | 1 | 1 | 1 |
| util.Parser.Parser() | 0 | 1 | 1 | 1 |
| util.Parser.Parser(Lexer) | 0 | 1 | 1 | 1 |
| util.Parser.Parser(Lexer, MapSet) | 0 | 1 | 1 | 1 |
| util.Parser.parseDeri() | 0 | 1 | 1 | 1 |
| util.Parser.parseExp() | 5 | 1 | 4 | 4 |
| util.Parser.parseExpr() | 2 | 1 | 3 | 3 |
| util.Parser.parseExprFactor() | 3 | 1 | 3 | 3 |
| util.Parser.parseFactor() | 6 | 6 | 6 | 6 |
| util.Parser.parseFunctor() | 1 | 1 | 2 | 2 |
| util.Parser.parseMapping() | 1 | 1 | 2 | 2 |
| util.Parser.parseNumber() | 4 | 1 | 3 | 4 |
| util.Parser.parsePower() | 3 | 1 | 3 | 3 |
| util.Parser.parseTerm() | 5 | 1 | 4 | 5 |
| util.Parser.setLexer(Lexer) | 0 | 1 | 1 | 1 |
| util.Parser.setMapSet(MapSet) | 0 | 1 | 1 | 1 |
| util.Tools.bigInteger2Int(BigInteger) | 0 | 1 | 1 | 1 |
| util.Tools.expFilter(String) | 6 | 7 | 7 | 7 |
| util.Tools.postProcess(String) | 2 | 1 | 3 | 3 |
| util.Tools.preProcess(String) | 16 | 3 | 5 | 9 |
| util.Tools.simplifiedString(String) | 38 | 6 | 10 | 11 |
| util.Tools.trim(String) | 0 | 1 | 1 | 1 |
| Class | OCavg | OCmax | WMC | |
| Main | 2.00 | 2 | 2 | |
| expression.Deri | 1.25 | 3 | 20 | |
| expression.Exp | 1.30 | 4 | 26 | |
| expression.Expr | 2.53 | 9 | 48 | |
| expression.FactorType | n/a | n/a | 0 | |
| expression.Functor | 2.53 | 9 | 38 | |
| expression.Number | 1.44 | 3 | 13 | |
| expression.Power | 2.21 | 9 | 31 | |
| expression.Term | 4.00 | 23 | 112 | |
| func.MapSet | 1.17 | 2 | 7 | |
| func.Mapping | 1.33 | 3 | 12 | |
| util.IoManager | 1.00 | 1 | 3 | |
| util.Lexer | 2.40 | 4 | 12 | |
| util.Parser | 2.33 | 6 | 35 | |
| util.Tools | 4.83 | 11 | 29 | |
| Package | v(G)avg | v(G)tot | ||
| 2.00 | 2 | |||
| expression | 2.44 | 295 | ||
| func | 1.27 | 19 | ||
| util | 3.00 | 87 | ||
| Module | v(G)avg | v(G)tot | ||
| lab1 | 2.43 | 403 | ||
| Project | v(G)avg | v(G)tot | ||
| project | 2.43 | 403 |
由idea插件计算出的指标来看,Term类的复杂度较高、方法中涉及到多个因子和递归调用的部分复杂度较高

由代码量统计也可以看出,Term类中代码行数最多,由于Term需要管理所有的因子,其中的耦合度也会较高,这给后期代码的维护带来了很多问题
从现在的架构出发,如果要加入一个新的因子,实际上比较简单
simplify(),derive(),toString(),copy(),map()equals()Term中添加加入该因子的相关方法,修改Term中toString(),derive(),simplify()Parser中添加解析因子的方法如果要加入一种新的运算function,则
Factor.function()Term实现Term.function(),该函数调用所有因子的这个方法,形成运算结果Expr返回由于自己的疏忽,本单元的作业产生了大量的bug。
addAll导致深浅复制相关的bug在开发过程中,还产生过很多由于增量开发中没有完整改写相关方法导致的bug,这主要还是因为整个架构类与类之间的耦合度太高,方法冗杂导致的。
设计时,为了接口的精简导致了很多方法内部代码相当复杂,很容易在增量开发中因为疏忽没有完整的实现相关操作导致bug的产生。
另外,由于没有开发自动化测试工具,也导致了一些bug没有及时的被测试出来
互测中寻找bug最强大的工具还是自动化测试脚本(然而由于我没有写生成器,所以并没有产生什么有价值的结果)
被测出来的bug除了上述情况,主要还是糟糕的实现导致的性能问题(在多个exp嵌套的情况下会超时)
虽然排序实际上可以有效的降低判断表达式是否相等的时间复杂度,但是由于重写compareTo很有可能导致更严重更隐秘的bug,因此没有做这样的实现
关于性能分的优化,我采取的策略是
在toString()方法过程中输出最复杂的表达式
在toString()输出结尾通过后处理的方式,直接用正则表达式匹配输出结果来进行特殊情况的化简,例如在对exp的化简中
public static String expFilter(String str) {
Matcher matcher = expPattern1.matcher(str);
if (matcher.find()) {
return "exp(" + matcher.group(2) + ")^" + matcher.group(1);
}
// ...
matcher = expPattern6.matcher(str);
if (matcher.find()) {
return "exp(" + matcher.group(2) + "^" + matcher.group(3)
+ ")^" + matcher.group(1);
}
return str;
}
显然,从性能的角度上这样处理并不好,但是这样处理拥有更好的可扩展性,同时在产生bug时,由于产生的原始字符串是完整形式,更容易进行debug
这次写的不满意。
总而言之,这个单元的作业写的并不好,下个单元要整改
下个单元见!