301
社区成员
发帖
与我相关
我的任务
分享本单元的主要任务是表达式的解析与展开,hw1为单变量多项式的展开,hw2为含指数函数与自定义函数的展开,hw3新增求导要求,总的说来,从hw1迭代到hw2的难度最大,也是本人最痛的一次经历(哭)。下面是我的具体思考

根据上图(图中类的方法属性放在了下文分析),我们可以看出笔者设计的框架有8个类,1个接口,其中:
Variable类有1属性:String var,2方法:
Number类有1属性:BigInteger num,3方法:
Expr类有1属性:HashMap<Integer, BigInteger> termmap,6方法:
Term类有1属性HashMap<Integer, BigInteger> factormap,4方法:
Poly类有4方法:
Lexer类有3属性:String input,int pos,String curToken,3方法:
Parser类有1属性Lexer lexer,4方法:
Main类有2方法:
值得注意的是,前四个类Expr,Number,Var,Term类均实现了接口,便于统一管理
笔者分析插件采用的是MetricReloaded,主要从以下方面分析:
利用MetricReloaded分析本人设计的方法如图,可以发现以下问题:
| Method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| Expr.Expr() | 0 | 1 | 1 | 1 |
| Expr.addTerm(Term) | 1 | 1 | 2 | 2 |
| Expr.getmap() | 0 | 1 | 1 | 1 |
| Expr.initial(BigInteger) | 0 | 1 | 1 | 1 |
| Expr.initial1() | 0 | 1 | 1 | 1 |
| Expr.simplification(Expr) | 25 | 1 | 8 | 9 |
| Lexer.Lexer(String) | 0 | 1 | 1 | 1 |
| Lexer.getCurToken() | 0 | 1 | 1 | 1 |
| Lexer.next() | 9 | 1 | 7 | 7 |
| Main.MoreStandardized(String) | 0 | 1 | 1 | 1 |
| Main.main(String[]) | 0 | 1 | 1 | 1 |
| Number.Number(BigInteger) | 0 | 1 | 1 | 1 |
| Number.getNum() | 0 | 1 | 1 | 1 |
| Number.toString() | 0 | 1 | 1 | 1 |
| Parser.Parser(Lexer) | 0 | 1 | 1 | 1 |
| Parser.parseExpr() | 2 | 1 | 3 | 3 |
| Parser.parseFactor() | 4 | 2 | 2 | 3 |
| Parser.parseTerm() | 23 | 1 | 9 | 11 |
| Poly.add(HashMap<Integer, BigInteger>, HashMap<Integer, BigInteger>) | 3 | 1 | 3 | 3 |
| Poly.multiply(HashMap<Integer, BigInteger>, HashMap<Integer, BigInteger>) | 6 | 1 | 4 | 4 |
| Poly.power(HashMap<Integer, BigInteger>, int) | 2 | 2 | 3 | 3 |
| Poly.subtract(HashMap<Integer, BigInteger>, HashMap<Integer, BigInteger>) | 3 | 1 | 3 | 3 |
| Term.Term() | 0 | 1 | 1 | 1 |
| Term.addFactor(Factor) | 5 | 1 | 4 | 4 |
| Term.forinitial(int) | 2 | 1 | 2 | 2 |
| Term.getmap() | 0 | 1 | 1 | 1 |
| Variable.Variable(String) | 0 | 1 | 1 | 1 |
| Variable.toString() | 0 | 1 | 1 | 1 |
| t.ma() | 0 | 1 | 1 | 1 |
利用MetricReloaded分析本人设计的类如图,可以发现以下问题:
| Class | OCavg | OCmax | WMC |
|---|---|---|---|
| Expr | 2.5 | 9 | 15 |
| Lexer | 2.67 | 6 | 8 |
| Main | 1 | 1 | 2 |
| Number | 1 | 1 | 3 |
| Parser | 4.75 | 12 | 19 |
| Poly | 3.25 | 4 | 13 |
| Term | 2 | 4 | 8 |
| Variable | 1 | 1 | 2 |
| t | 1 | 1 | 1 |
简而言之,笔者采用的架构是将解析建语法树与计算分开的架构,因为在解析完表达式建完语法树后,所有的计算都是先转化为poly结构,再利用poly的特性计算
再建语法树时,利用lexer和parser,以lexer.next方法逐个字符是识别并返回一个token语法单元,然后parser根据语法定义,采用递归性下降的方式,采用Expr-》Term-》factor-》(Number/Var/Expr)架构不断下降,直至最底层,将返回的token建树,那么,很容易发现,这样的语法树以一个Expr为根节点,每一个分叉均是Term,每一个Term分支均是factor,如果说factor是常量或变量,则作为叶节点返回,但如果是Expr,则该factor是一个子树的根节点,一直递归下去,只知道叶节点,由此,我们便得到一颗以Expr为根节点的树。
计算时,我们给Expr,Term等类都添加一个topoly的方法,如epxr转化为poly,则利用terms里每一项Term转化为poly相加得到,Term转化为poly,则用每一项factor转化为poly相乘得到,factor如果时Expr,调用expr的topoly,如果是Number或Var,则直接返回。
优点:
缺点:

根据上图(这张图制作耗时最久),我们可以看出笔者设计的框架有12个类,1个接口,其中:
Var类有2属性:char name,BigInteger indexNumber,3方法:
Const类有1属性:BigInteger num,4方法:
Expr类有1属性:ArrayList terms,BigInteger indexNumber,5方法:
Expr()构造方法
void addTerm(Term term)将term加入termmap
void setIndexNumber(BigInteger indexNumber)用于初始化indexnumber
Poly toPoly()转化为poly结构
string toString()用于将该对象转化为String并return
Term类有2属性ArrayList factors,BigInteger coe,4方法:
Poly类有1属性:HashMap<Unit, BigInteger> unitList,4方法:
Exp类有2属性:Factor factor,BigInteger indexNumber,3个方法:
Func类有2属性:String funcExpr,Expr expr,4方法:
Unit类有4属性:BigInteger coe,HashMap<String, BigInteger> vars,HashMap<Poly, BigInteger> expMap,BigInteger mark,多方法:
FuncDefiner类有3属性:static HashMap<String, String> func,HashMap<String, ArrayList> params,HashMap<String, String> varMap,3方法:
Lexer类有3属性:String input,int pos,5方法:
Parser类有1属性Lexer lexer,多方法:
Main类有5方法:
值得注意的是,前5个类Expr,Number,Var,Term类均实现了接口,便于统一管理
同hw1,笔者分析插件采用的是MetricReloaded,这里就不重复介绍了
利用MetricReloaded分析本人设计的方法如图,可以发现以下问题:
findGreatestCommonFactor和parseFactor ev(G)复杂度高,在于方法长而复杂
Unit.toString,simplifySubExpression的iv(G),v(G)复杂度高,原因在于频繁调用其他类,耦合度高
| Method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| MainClass.coeMulFac(String) | 30 | 1 | 10 | 10 |
| MainClass.findGreatestCommonFactor(List) | 11 | 6 | 5 | 8 |
| MainClass.main(String[]) | 1 | 1 | 2 | 2 |
| MainClass.simplifyExpression(String) | 4 | 1 | 3 | 3 |
| MainClass.simplifySubExpression(String) | 37 | 1 | 12 | 12 |
| expr.Const.Const(String) | 0 | 1 | 1 | 1 |
| expr.Const.getNum() | 0 | 1 | 1 | 1 |
| expr.Const.toPoly() | 0 | 1 | 1 | 1 |
| expr.Const.toString() | 0 | 1 | 1 | 1 |
| expr.Exp.Exp(Factor, String) | 0 | 1 | 1 | 1 |
| expr.Exp.toPoly() | 3 | 2 | 3 | 3 |
| expr.Exp.toString() | 1 | 1 | 2 | 2 |
| expr.Expr.Expr() | 0 | 1 | 1 | 1 |
| expr.Expr.addTerm(Term) | 0 | 1 | 1 | 1 |
| expr.Expr.setIndexNumber(BigInteger) | 0 | 1 | 1 | 1 |
| expr.Expr.toPoly() | 2 | 2 | 3 | 3 |
| expr.Expr.toString() | 1 | 1 | 2 | 2 |
| expr.Func.Func(String, ArrayList) | 0 | 1 | 1 | 1 |
| expr.Func.parse(String) | 0 | 1 | 1 | 1 |
| expr.Func.toPoly() | 0 | 1 | 1 | 1 |
| expr.Func.toString() | 0 | 1 | 1 | 1 |
| expr.Poly.Poly() | 0 | 1 | 1 | 1 |
| expr.Poly.addPoly(Poly) | 2 | 1 | 3 | 3 |
| expr.Poly.addUnit(Unit) | 2 | 1 | 2 | 2 |
| expr.Poly.equals(Object) | 3 | 3 | 2 | 4 |
| expr.Poly.hashCode() | 0 | 1 | 1 | 1 |
| expr.Poly.isFactor() | 3 | 3 | 3 | 3 |
| expr.Poly.mulPoly(Poly) | 3 | 1 | 3 | 3 |
| expr.Poly.powPoly(BigInteger) | 3 | 1 | 3 | 3 |
| expr.Poly.setMark() | 2 | 1 | 3 | 3 |
| expr.Poly.toString() | 3 | 3 | 3 | 4 |
| expr.Term.Term(char) | 2 | 1 | 1 | 2 |
| expr.Term.addFactor(Factor) | 0 | 1 | 1 | 1 |
| expr.Term.inverse(char) | 1 | 1 | 2 | 2 |
| expr.Term.mergeConst() | 3 | 1 | 3 | 3 |
| expr.Term.toPoly() | 1 | 1 | 2 | 2 |
| expr.Term.toString() | 6 | 1 | 5 | 5 |
| expr.Unit.Unit(BigInteger) | 0 | 1 | 1 | 1 |
| expr.Unit.addExp(Poly, BigInteger) | 2 | 1 | 3 | 3 |
| expr.Unit.addVar(String, BigInteger) | 2 | 1 | 3 | 3 |
| expr.Unit.equals(Object) | 3 | 3 | 2 | 4 |
| expr.Unit.getCoe() | 0 | 1 | 1 | 1 |
| expr.Unit.hashCode() | 0 | 1 | 1 | 1 |
| expr.Unit.isFactor() | 4 | 4 | 3 | 4 |
| expr.Unit.mulUnit(Unit) | 4 | 1 | 5 | 5 |
| expr.Unit.setCoe(BigInteger) | 0 | 1 | 1 | 1 |
| expr.Unit.setMark() | 0 | 1 | 1 | 1 |
| expr.Unit.size() | 0 | 1 | 1 | 1 |
| expr.Unit.toString() | 22 | 3 | 11 | 11 |
| expr.Var.Var(char, String) | 0 | 1 | 1 | 1 |
| expr.Var.toPoly() | 0 | 1 | 1 | 1 |
| expr.Var.toString() | 0 | 1 | 1 | 1 |
| parser.FuncDefiner.addFunc(String) | 8 | 1 | 7 | 7 |
| parser.FuncDefiner.callFunc(String, ArrayList) | 1 | 1 | 2 | 2 |
| parser.FuncDefiner.simplifyExpression(String) | 1 | 1 | 2 | 2 |
| parser.Lexer.Lexer(String) | 0 | 1 | 1 | 1 |
| parser.Lexer.getChar() | 0 | 1 | 1 | 1 |
| parser.Lexer.getConst() | 2 | 1 | 3 | 3 |
| parser.Lexer.getString(int) | 0 | 1 | 1 | 1 |
| parser.Lexer.peek() | 2 | 2 | 2 | 2 |
| parser.Parser.Parser(Lexer) | 0 | 1 | 1 | 1 |
| parser.Parser.parseConst() | 4 | 2 | 4 | 4 |
| parser.Parser.parseExp() | 1 | 2 | 1 | 2 |
| parser.Parser.parseExpr() | 2 | 1 | 3 | 3 |
| parser.Parser.parseFactor() | 8 | 5 | 10 | 10 |
| parser.Parser.parseFunc() | 1 | 1 | 2 | 2 |
| parser.Parser.parseIndexNumber() | 5 | 2 | 4 | 4 |
| parser.Parser.parseTerm() | 5 | 1 | 6 | 6 |
| parser.Parser.parseVar() | 0 | 1 | 1 | 1 |
利用MetricReloaded分析本人设计的类如图,可以发现以下问题:
| Class | OCavg | OCmax | WMC |
|---|---|---|---|
| MainClass | 6.8 | 12 | 34 |
| expr.Const | 1 | 1 | 4 |
| expr.Exp | 2 | 3 | 6 |
| expr.Expr | 1.6 | 3 | 8 |
| expr.Func | 1 | 1 | 4 |
| expr.Poly | 2.6 | 4 | 26 |
| expr.Term | 2.5 | 5 | 15 |
| expr.Unit | 2.92 | 11 | 35 |
| expr.Var | 1 | 1 | 3 |
| parser.FuncDefiner | 3 | 5 | 9 |
| parser.Lexer | 1.4 | 2 | 7 |
先介绍下基本形式:
举例 通用形式
因子:
Const: 3 (num)
Var: x^2 (name^indexnumber)
Expr: (x+1)^2 ((term1+term2+...+termn)^indexnumber)
Exp: exp((x+1))^2 (exp(factor)^indexnumber)
Func: f(x)----->x+1 (String funcExpr对应树结构expr)
项:
term: 2*x*x (coe*factor1*factor2*...*factorn)
表达式:
Expr: exp((x+1))^2 (exp(factor)^indexnumber)
单元:
unit 2*x^2*exp((x+1)) (coe*(var^index)*exp(poly))^indexnumber
多项式:
poly: (2*x^2*exp((x+1))+3*x^3*exp((2*x+1))) (unit+unit)
简而言之,笔者采用的架构是将解析建语法树与计算分开的架构,因为在解析完表达式建完语法树后,所有的计算都是先转化为poly结构,再利用poly的特性计算
建语法树时,基于迭代性好,思路和hw1差不多,但额外多了对exp,func的解析
计算时,给Expr,Term等类都添加一个topoly的方法,如:
Const topoly:new unit,并将num传入作为系数,然后将new unit加入poly的unitlist即可
Var topoly:先创建一个系数为1的unit,然后调用unit的addvar即可
Expr topoly:分expr indexnumber为0和不为0两种,后者实现:将expr每个term topoly然后add,最后调用poly.powPoly(indexNumber)即可
Exp topoly:主要是调用factor.toPoly()方法
输出时,给每个类添加toString方法:
具体思路是利用因子,项,表达式的基本形式结合对应属性,构建空Biginteger,然后append即可,由于比较简单,我就不一一讲解
优点:
缺点:

根据上图,我们可以看出笔者设计的框架有15个类,2个接口,其中:
Var类有2属性:char name,BigInteger indexNumber,3方法:
Const类有1属性:BigInteger num,4方法:
Expr类有2属性:ArrayList terms,BigInteger indexNumber,5方法:
Expr()构造方法
void addTerm(Term term)将term加入termmap
void setIndexNumber(BigInteger indexNumber)用于初始化indexnumber
Poly toPoly()转化为poly结构
string toString()用于将该对象转化为String并return
Term类有2属性ArrayList factors,BigInteger coe,4方法:
Poly类有1属性:HashMap<Unit, BigInteger> unitList,4方法:
Exp类有2属性:Factor factor,BigInteger indexNumber,3个方法:
Func类有2属性:String funcExpr,Expr expr,4方法:
Unit类有4属性:BigInteger coe,HashMap<String, BigInteger> vars,HashMap<Poly, BigInteger> expMap,BigInteger mark,多方法:
FuncDefiner类有3属性:static HashMap<String, String> func,HashMap<String, ArrayList> params,HashMap<String, String> varMap,3方法:
Lexer类有3属性:String input,int pos,5方法:
Parser类有1属性Lexer lexer,多方法:
Main类有5方法:
不难发现,本人hw3各个类的属性和方法和hw2的额差别不大,是指新增了部分类和求导方法,hw2设计的类和方法在hw3依然可以用,这体现了好的迭代性的重要性
值得注意的是,前5个类Expr,Number,Var,Term等类均实现了接口Factor,2个类实现了接口Derivative,便于统一管理
利用MetricReloaded分析本人设计的方法如图,可以发现以下问题:
findGreatestCommonFactor ev(G)复杂度高,在于方法长而复杂
Unit.toString,simplifySubExpression和parseFactor的iv(G),v(G)复杂度高,原因在于频繁调用其他类,耦合度高
| Method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| Deri.Deri(Expr) | 0 | 1 | 1 | 1 |
| Deri.toPolynomial() | 0 | 1 | 1 | 1 |
| Deri.toString() | 0 | 1 | 1 | 1 |
| Exp.Exp(Factor, String) | 0 | 1 | 1 | 1 |
| Exp.Exp(Poly, String) | 0 | 1 | 1 | 1 |
| Exp.differential() | 5 | 1 | 4 | 4 |
| Exp.toPolynomial() | 4 | 3 | 4 | 4 |
| Exp.toString() | 4 | 2 | 3 | 3 |
| Expr.Expr() | 0 | 1 | 1 | 1 |
| Expr.addTerm(Term) | 0 | 1 | 1 | 1 |
| Expr.setIndexNumber(BigInteger) | 0 | 1 | 1 | 1 |
| Expr.toPolynomial() | 2 | 2 | 3 | 3 |
| Expr.toString() | 1 | 1 | 2 | 2 |
| Func.Func(String, ArrayList) | 0 | 1 | 1 | 1 |
| Func.parse(String) | 0 | 1 | 1 | 1 |
| Func.toPolynomial() | 0 | 1 | 1 | 1 |
| Func.toString() | 0 | 1 | 1 | 1 |
| FuncDefiner.addFunc(String) | 12 | 1 | 9 | 9 |
| FuncDefiner.callFunction(String, ArrayList) | 1 | 1 | 2 | 2 |
| FuncDefiner.simplifyExpression(String) | 1 | 1 | 2 | 2 |
| Lexer.Lexer(String) | 0 | 1 | 1 | 1 |
| Lexer.nextChar() | 0 | 1 | 1 | 1 |
| Lexer.nextNumber() | 2 | 1 | 3 | 3 |
| Lexer.nextString(int) | 0 | 1 | 1 | 1 |
| Lexer.peek() | 2 | 2 | 2 | 2 |
| Main.MoreStandardized(String) | 0 | 1 | 1 | 1 |
| Main.coeMulFac(String) | 15 | 1 | 6 | 6 |
| Main.f(List, List) | 13 | 1 | 6 | 6 |
| Main.ff(String, List, List) | 29 | 1 | 9 | 9 |
| Main.findGreatestCommonFactor(List) | 11 | 6 | 5 | 8 |
| Main.main(String[]) | 1 | 1 | 2 | 2 |
| Main.simplifyExpression(String) | 4 | 1 | 3 | 3 |
| Main.simplifySubExpression(String) | 11 | 1 | 6 | 6 |
| Number.Number(String) | 0 | 1 | 1 | 1 |
| Number.getNumber() | 0 | 1 | 1 | 1 |
| Number.toPolynomial() | 0 | 1 | 1 | 1 |
| Number.toString() | 0 | 1 | 1 | 1 |
| Parser.Parser(Lexer) | 0 | 1 | 1 | 1 |
| Parser.parseDeri() | 0 | 1 | 1 | 1 |
| Parser.parseExpr() | 2 | 1 | 3 | 3 |
| Parser.parseFactor() | 14 | 7 | 12 | 13 |
| Parser.parseFunc() | 1 | 1 | 2 | 2 |
| Parser.parseIndexNumber() | 5 | 2 | 4 | 4 |
| Parser.parseTerm() | 4 | 1 | 5 | 5 |
| Poly.Poly() | 0 | 1 | 1 | 1 |
| Poly.addPoly(Poly) | 2 | 1 | 3 | 3 |
| Poly.addUnit(Unit) | 2 | 1 | 2 | 2 |
| Poly.differential() | 1 | 1 | 2 | 2 |
| Poly.equals(Object) | 3 | 3 | 2 | 4 |
| Poly.getUnitlist() | 0 | 1 | 1 | 1 |
| Poly.hashCode() | 0 | 1 | 1 | 1 |
| Poly.isFactor() | 7 | 4 | 4 | 4 |
| Poly.mulPoly(Poly) | 3 | 1 | 3 | 3 |
| Poly.powPoly(BigInteger) | 3 | 1 | 3 | 3 |
| Poly.setMark() | 2 | 1 | 3 | 3 |
| Poly.toString() | 3 | 3 | 3 | 4 |
| Term.Term(char) | 2 | 1 | 1 | 2 |
| Term.addFactor(Factor) | 0 | 1 | 1 | 1 |
| Term.mergeNumber() | 3 | 1 | 3 | 3 |
| Term.reverse(char) | 1 | 1 | 2 | 2 |
| Term.toPolynomial() | 1 | 1 | 2 | 2 |
| Term.toString() | 6 | 1 | 5 | 5 |
| Unit.Unit(BigInteger) | 0 | 1 | 1 | 1 |
| Unit.addExp(Poly, BigInteger) | 2 | 1 | 3 | 3 |
| Unit.addVar(String, BigInteger) | 2 | 1 | 3 | 3 |
| Unit.differential() | 8 | 1 | 6 | 6 |
| Unit.equals(Object) | 3 | 3 | 2 | 4 |
| Unit.getCoe() | 0 | 1 | 1 | 1 |
| Unit.getExpmap() | 0 | 1 | 1 | 1 |
| Unit.getSize() | 0 | 1 | 1 | 1 |
| Unit.getVars() | 0 | 1 | 1 | 1 |
| Unit.hashCode() | 0 | 1 | 1 | 1 |
| Unit.isFactor() | 4 | 4 | 3 | 4 |
| Unit.mulUnit(Unit) | 4 | 1 | 5 | 5 |
| Unit.setCoe(BigInteger) | 0 | 1 | 1 | 1 |
| Unit.setExpmap(HashMap<Poly, BigInteger>) | 0 | 1 | 1 | 1 |
| Unit.setMark() | 0 | 1 | 1 | 1 |
| Unit.setVars(HashMap<String, BigInteger>) | 0 | 1 | 1 | 1 |
| Unit.toString() | 22 | 3 | 11 | 11 |
| Var.Var(char, String) | 0 | 1 | 1 | 1 |
| Var.differential() | 0 | 1 | 1 | 1 |
| Var.toPolynomial() | 1 | 2 | 2 | 2 |
| Var.toString() | 0 | 1 | 1 | 1 |
利用MetricReloaded分析本人设计的类如图,可以发现以下问题:
| Class | OCavg | OCmax | WMC |
|---|---|---|---|
| Deri | 1 | 1 | 3 |
| Exp | 2.6 | 4 | 13 |
| Expr | 1.6 | 3 | 8 |
| Func | 1 | 1 | 4 |
| FuncDefiner | 3.67 | 7 | 11 |
| Lexer | 1.4 | 2 | 7 |
| Main | 5 | 9 | 40 |
| Number | 1 | 1 | 4 |
| Parser | 3 | 8 | 21 |
| Poly | 2.5 | 4 | 30 |
| Term | 2.5 | 5 | 15 |
| Unit | 2.65 | 11 | 45 |
| Var | 1.25 | 2 | 5 |
和hw2的架构基本一致,多了求导部分,新增Deri类实现expr的求导功能,返回求导后的poly结构,新增Derivative接口管理Var,Exp类,实现var和exp的求导
优点:
缺点:
本人互测时成功hack了别人多次,举个hack的输入样例:
0
(x ^ +004 + -9 + -9 + x ^ +004 - 7 + x ^ 7 - x ^ +0 - -3 - -14)^+2
错误原因为房友没有正确处理 x ^ +0
本人互测时成功hack了别人2次,举个hack的输入样例:
0
99999999999999999999999-99999999999999999999999
错误原因为房友用int而不是Biginteger
本人互测时成功hack了别人0次,不得不感叹群友太强了
hw1:将计算方式有项间两两相乘改为利用hashmap实现运算,速度和性能上提升
hw2:实现如exp((10+10x))优化为exp((1+x))^10
建议: