383
社区成员
Expr
,Num
,Vari
都实现 Factor
的接口,实现 simplify
方法, 利用 HashMap<SimpleExpr, Integer>
来维护最简形式的表达式,作为 $key$ 的 SimpleExpr
的属性为一个三元数组,为一个项 x
, y
, z
的指数组成的 3 元数对, $value$ 为该项系数., SimpleExpr
需要实现乘法功能(指数相加即可)。
同时Expr需要实现 toString
方法,这个方法根据 simplify
的结果输出。
支持 parseExpr, parseTerm, parseFactor, parseVari, parseNum
各个方法即可。
对于空白符,不事先全部忽略,仅仅在需要处调用 lexer
的 ignorespace
方法,忽略接下来的若干个空格。这使得对于格式合法与否的判断更加严谨。如 x**+ 001
实际上不是一个合法的表达式,但是如果事先忽略所有空格则误判为合法。虽然本单元对于格式合法性的判断并没有要求,这样的处理方法似乎略显繁琐,实则不然,这样的设计提高了程序的健壮性,更能满足进一步开发的需求。
Parser.parseVari() | 4.0 | 1.0 | 3.0 | 3.0 |
---|---|---|---|---|
Parser.parseTerm() | 5.0 | 1.0 | 5.0 | 5.0 |
Parser.Parser(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.parseNum() | 2.0 | 3.0 | 3.0 | 3.0 |
Parser.parseFactor() | 10.0 | 4.0 | 6.0 | 7.0 |
Parser.parseExpr() | 8.0 | 4.0 | 6.0 | 6.0 |
Main.main(String[]) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.next() | 1.0 | 2.0 | 1.0 | 2.0 |
Lexer.nEnd() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.ignoreSpace() | 3.0 | 2.0 | 2.0 | 4.0 |
Lexer.getNum() | 3.0 | 1.0 | 3.0 | 4.0 |
Lexer.getIndex() | 3.0 | 3.0 | 2.0 | 4.0 |
Lexer.cur() | 1.0 | 1.0 | 1.0 | 2.0 |
expr.Vari.Vari(String, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Vari.simplify() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Term.simplify() | 2.0 | 1.0 | 3.0 | 3.0 |
expr.Term.negate(HashMap) | 1.0 | 1.0 | 2.0 | 2.0 |
expr.Term.mult(HashMap, HashMap) | 3.0 | 1.0 | 3.0 | 3.0 |
expr.Term.clear(HashMap) | 4.0 | 1.0 | 4.0 | 4.0 |
expr.Term.addFactor(Factor, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.tr(BigInteger) | 16.0 | 5.0 | 5.0 | 11.0 |
expr.SimpleExpr.SimpleExpr(int[]) | 1.0 | 1.0 | 1.0 | 2.0 |
expr.SimpleExpr.SimpleExpr() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.mul(SimpleExpr) | 1.0 | 1.0 | 1.0 | 2.0 |
expr.SimpleExpr.hashCode() | 1.0 | 2.0 | 1.0 | 2.0 |
expr.SimpleExpr.equals(Object) | 7.0 | 5.0 | 1.0 | 5.0 |
expr.Num.simplify() | 1.0 | 1.0 | 2.0 | 2.0 |
expr.Num.Num(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.tString() | 13.0 | 4.0 | 6.0 | 8.0 |
expr.Expr.simplify() | 4.0 | 3.0 | 4.0 | 5.0 |
expr.Expr.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.mult(HashMap, HashMap) | 3.0 | 1.0 | 3.0 | 3.0 |
expr.Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.clear(HashMap) | 4.0 | 1.0 | 4.0 | 4.0 |
expr.Expr.addTerm(Term, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.add(HashMap, HashMap, int) | 3.0 | 1.0 | 3.0 | 3.0 |
对于化简结果为 0 的情况输出为空,需要进行特判。
本次加入了三角函数和自定义函数需求。
加入新类 Tri
(三角函数), 实现 Factor
接口, 属性中包含了一个 Expr。
同时原来的 SimpleExpr
设计中仅仅只有三元数组,不能继续满足需求,所以加入两个 HashMap<Expr, Integer>
来支持 $\Pi \sin Expr\Pi\cos Expr$ 形式的存储,同样 $varlue$ 用来表示系数。
SimpleExpr
的比较也需要经历重写,不但比较三元数组,同时需要比较 Expr
和对应的 $value$ , 这需要递归实现。
为 Parser
新增一个参数 HashMap<Character, String> arg
, 建立自变量和参数因子字符串对应关系。解析表达式是,最外层即为自己对应自己,而遇到了函数调用,根据形式创建新的 Parser
, 设定好 arg
, 这个 Parser
会解析函数表达式,并在遇到变量因子时去解析对应的字符串,从而返回正确的结果。
这个设计其实并不健全,它本质上将函数调用处理和语义解析杂糅在了一起, 这中不健全的缺陷在hw3中完全暴露,最终导致了一次代码重构。
Parser.parseVari() | 6.0 | 2.0 | 4.0 | 4.0 |
---|---|---|---|---|
Parser.parseTri() | 17.0 | 1.0 | 5.0 | 11.0 |
Parser.parseTerm() | 5.0 | 1.0 | 5.0 | 5.0 |
Parser.Parser(Lexer, ArrayList, ArrayList, HashMap) | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.Parser(Lexer) | 1.0 | 1.0 | 2.0 | 2.0 |
Parser.parseNum() | 2.0 | 3.0 | 3.0 | 3.0 |
Parser.parseFuncDef() | 11.0 | 3.0 | 2.0 | 9.0 |
Parser.parseFuncCall() | 8.0 | 1.0 | 4.0 | 7.0 |
Parser.parseFactor() | 12.0 | 6.0 | 8.0 | 9.0 |
Parser.parseExpr() | 8.0 | 4.0 | 6.0 | 6.0 |
Parser.changeLexer(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
Main.main(String[]) | 1.0 | 1.0 | 2.0 | 2.0 |
Lexer.rest() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.next() | 1.0 | 2.0 | 1.0 | 2.0 |
Lexer.nEnd() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.ignoreSpace() | 3.0 | 2.0 | 2.0 | 4.0 |
Lexer.getNum() | 3.0 | 1.0 | 3.0 | 4.0 |
Lexer.getIndex() | 3.0 | 3.0 | 2.0 | 4.0 |
Lexer.cur() | 1.0 | 1.0 | 1.0 | 2.0 |
expr.Vari.Vari(String, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Vari.simplify() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Tri.Tri(int, int, Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Tri.simplify() | 4.0 | 2.0 | 4.0 | 5.0 |
expr.Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Term.simplify() | 3.0 | 2.0 | 3.0 | 4.0 |
expr.Term.negate(HashMap) | 1.0 | 1.0 | 2.0 | 2.0 |
expr.Term.mult(HashMap, HashMap) | 19.0 | 5.0 | 7.0 | 7.0 |
expr.Term.clear(HashMap) | 5.0 | 1.0 | 5.0 | 5.0 |
expr.Term.addFactor(Factor, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.zero() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.tr(BigInteger) | 26.0 | 5.0 | 11.0 | 17.0 |
expr.SimpleExpr.SimpleExpr(int[]) | 1.0 | 1.0 | 1.0 | 2.0 |
expr.SimpleExpr.SimpleExpr() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.mul(SimpleExpr) | 5.0 | 1.0 | 5.0 | 6.0 |
expr.SimpleExpr.getcoe() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.cmp(SimpleExpr) | 32.0 | 15.0 | 9.0 | 17.0 |
expr.SimpleExpr.appendSin(Expr, int) | 12.0 | 5.0 | 4.0 | 9.0 |
expr.SimpleExpr.appendCos(Expr, int) | 6.0 | 5.0 | 3.0 | 6.0 |
expr.Num.simplify() | 1.0 | 1.0 | 2.0 | 2.0 |
expr.Num.Num(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.tString() | 13.0 | 4.0 | 6.0 | 8.0 |
expr.Expr.simplify() | 5.0 | 4.0 | 4.0 | 6.0 |
expr.Expr.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.mult(HashMap, HashMap) | 19.0 | 5.0 | 7.0 | 7.0 |
expr.Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.cmp(Expr) | 22.0 | 9.0 | 5.0 | 12.0 |
expr.Expr.clear(HashMap) | 5.0 | 1.0 | 5.0 | 5.0 |
expr.Expr.addTerm(Term, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.add(HashMap, HashMap, int) | 16.0 | 4.0 | 7.0 | 7.0 |
在比较 SimpleExpr
和 Expr
的过程中,需要比较两个 HashMap
(记为 map1
和 map2
是否相同。笔者的实现时枚举 map1
中的 $key$ ,将其在两个 HashMap
中的 $value$ 进行比较。
然而这只能判断 map1
是否为 map2
的子集,需要额外对二者的 keySet
大小比较才能正确判断。
本次加入了求导的需求, 同时函数表达式可以包含之前定义过的表达式。
对有求导需求的 Expr
, 先简化为 HashMap<SimpleExpr, Integer>
再求导,同样需要递归实现。
新加入的求导功能似乎实现起来十分直观简明,只需为一些类添加求导方法即可。然而捋一捋求导,化简,调用函数三者的依赖关系,求导前要先化简,化简前要调用函数。那么,如果函数表达式中出现了求导运算会发生什么呢?糟糕!依赖关系成环了!
引发这场灾难的原因是前文提到的设计缺陷:函数调用处理和语义分析的界限被模糊了,原本两个可以更为独立的过程形成了一个微妙的“相互依赖”的关系,在hw2中,由于这种“相互依赖”是基于不同层次之间的,暂时没有让依赖图上产生环,但是它使得依赖关系变得臃肿而难以扩展,一旦一种跨层次的依赖被引入,架构便发生毁灭性的打击。
于是笔者重构了函数调用的实现。
新增实现 Factor
接口的 Func
用于表示函数因子,属性包括表达式和参数。这样程序就可以无后顾之忧地先化简表达式,处理好其中的求导操作,然后再将参数代入了。
Parser.parseVari() | 4.0 | 1.0 | 3.0 | 3.0 |
---|---|---|---|---|
Parser.parseTri() | 17.0 | 1.0 | 5.0 | 11.0 |
Parser.parseTerm() | 5.0 | 1.0 | 5.0 | 5.0 |
Parser.Parser(Lexer, ArrayList, ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
Parser.Parser(Lexer) | 1.0 | 1.0 | 2.0 | 2.0 |
Parser.parseNum() | 2.0 | 3.0 | 3.0 | 3.0 |
Parser.parseFuncDef() | 11.0 | 3.0 | 2.0 | 9.0 |
Parser.parseFuncCall() | 8.0 | 1.0 | 4.0 | 7.0 |
Parser.parseFactor() | 13.0 | 7.0 | 9.0 | 10.0 |
Parser.parseExpr() | 8.0 | 4.0 | 6.0 | 6.0 |
Parser.parseDrExpr() | 2.0 | 1.0 | 1.0 | 3.0 |
Parser.changeLexer(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
Main.main(String[]) | 1.0 | 1.0 | 2.0 | 2.0 |
Lexer.rest() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.next() | 1.0 | 2.0 | 1.0 | 2.0 |
Lexer.nEnd() | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
Lexer.ignoreSpace() | 3.0 | 2.0 | 2.0 | 4.0 |
Lexer.getNum() | 3.0 | 1.0 | 3.0 | 4.0 |
Lexer.getIndex() | 3.0 | 3.0 | 2.0 | 4.0 |
Lexer.cur() | 1.0 | 1.0 | 1.0 | 2.0 |
expr.Vari.Vari(String, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Vari.simplify() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Tri.Tri(int, int, Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Tri.simplify() | 4.0 | 2.0 | 4.0 | 5.0 |
expr.Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Term.simplify() | 3.0 | 2.0 | 3.0 | 4.0 |
expr.Term.negate(HashMap) | 1.0 | 1.0 | 2.0 | 2.0 |
expr.Term.mult(HashMap, HashMap) | 19.0 | 5.0 | 7.0 | 7.0 |
expr.Term.clear(HashMap) | 5.0 | 1.0 | 5.0 | 5.0 |
expr.Term.addFactor(Factor, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.zero() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.tr(BigInteger) | 26.0 | 5.0 | 11.0 | 17.0 |
expr.SimpleExpr.substitute(HashMap) | 9.0 | 1.0 | 6.0 | 6.0 |
expr.SimpleExpr.SimpleExpr(int[]) | 1.0 | 1.0 | 1.0 | 2.0 |
expr.SimpleExpr.SimpleExpr() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.mult(HashMap, HashMap) | 19.0 | 5.0 | 7.0 | 7.0 |
expr.SimpleExpr.mul(SimpleExpr) | 5.0 | 1.0 | 5.0 | 6.0 |
expr.SimpleExpr.getcoe() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.SimpleExpr.derive(int) | 10.0 | 2.0 | 6.0 | 7.0 |
expr.SimpleExpr.cmp(SimpleExpr) | 32.0 | 15.0 | 9.0 | 17.0 |
expr.SimpleExpr.clone() | 1.0 | 1.0 | 1.0 | 2.0 |
expr.SimpleExpr.appendSin(Expr, int) | 12.0 | 5.0 | 4.0 | 9.0 |
expr.SimpleExpr.appendCos(Expr, int) | 6.0 | 5.0 | 3.0 | 6.0 |
expr.SimpleExpr.add(HashMap, HashMap) | 13.0 | 4.0 | 6.0 | 6.0 |
expr.Num.simplify() | 1.0 | 1.0 | 2.0 | 2.0 |
expr.Num.Num(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Func.simplify() | 3.0 | 1.0 | 3.0 | 3.0 |
expr.Func.Func(Expr, HashMap) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Func.add(HashMap, HashMap) | 13.0 | 4.0 | 6.0 | 6.0 |
expr.Expr.tString() | 13.0 | 4.0 | 6.0 | 8.0 |
expr.Expr.substitute(HashMap) | 3.0 | 1.0 | 3.0 | 3.0 |
expr.Expr.simplify() | 5.0 | 4.0 | 4.0 | 6.0 |
expr.Expr.setIndex(int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.mult(HashMap, HashMap) | 19.0 | 5.0 | 7.0 | 7.0 |
expr.Expr.Expr(HashMap) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.derive(int) | 3.0 | 1.0 | 3.0 | 3.0 |
expr.Expr.cmp(Expr) | 22.0 | 9.0 | 5.0 | 12.0 |
expr.Expr.clear(HashMap) | 5.0 | 1.0 | 5.0 | 5.0 |
expr.Expr.addTerm(Term, int) | 0.0 | 1.0 | 1.0 | 1.0 |
expr.Expr.add(HashMap, HashMap, int) | 16.0 | 4.0 | 7.0 | 7.0 |
expr.DrExpr.simplify() | 0.0 | 1.0 | 1.0 | 1.0 |
expr.DrExpr.DrExpr(int, Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
如前文所述,新加入的求导操作可能引发依赖混乱,如果不充分测试,不使用包含求导的函数,可能无法发现依赖层面的bug。