301
社区成员
发帖
与我相关
我的任务
分享
MainClass 为程序入口,读入字符串输入,将其交给 preProcessor 进行预处理。处理后的 input 交给 Parser 解析表达式。在自定义函数定义的行,input 则被交给 FunctionUtils,解析自定义函数定义。
Lexer 负责将输入进行词法分析,拆分成一个个不同类型的 Token,并将其返回给 Parser。
Parser 按照语义及层级顺序,解析出 Expr,Term, Factor
由于多种 Factor 都有整数指数属性,所以我是将 Factor 作为父类,
各种 Factor 作为子类。
TokenType 是 Token 类型的一个枚举类。
Poly 是多项式类,是进行多项式计算,化简,以及转化为字符串的类,有属性 TreeSet units.
Unit 是单项式类,或者说基本项类,在每次作业中含义不同。内部进行基本项的计算化简,toString。
Expr, term, Factor 及其子类均实现了 toPoly()方法,都能够转化为 Poly,以实现统一。这些类的 toString 方法也都是直接重写为toPoly().toString() .
基于度量来分析自己的程序结构
架构设计体验
分析自己程序的bug
分析自己发现别人程序bug所采用的策略
分析自己进行的优化
心得体会
未来方向

爆红并不多,似乎还不错?
Unit 类的 WMC 较高, 毕竟我在其中放了很多方法。
note: WMC 是 Weighted Method Count 的缩写,也称为权重方法计数。它是一种度量对象内部复杂性的度量指标,用于衡量一个类中方法的数量和复杂性。
WMC 的计算方法是将每个方法的复杂度(通常使用 McCabe 圈复杂度)加总起来得到一个总和,即为该类的 WMC 值。WMC 值越高,表示该类的方法更多且相对较复杂,可能需要更多的测试和维护工作。
通过计算 WMC 值,可以帮助我们了解一个类的复杂性、维护难度和可测试性等方面的信息。通常情况下,较高的 WMC 值可能意味着较高的复杂性和耦合性,可能需要进一步重构来使代码更加清晰和可维护。
爆红的方法
| method | CogC | ev(G) | iv(G) | v(G) |
| :------------------- | :--- | :---- | :---- | :--- |
| Lexer.tokenNext() | 16.0 | 7.0 | 8.0 | 15.0 |
| Parser.parseFactor() | 6.0 | 2.0 | 4.0 | 11.0 |
| Poly.addUnit(Unit) | 8.0 | 4.0 | 5.0 | 6.0 |
| Poly.compareTo(Poly) | 4.0 | 4.0 | 2.0 | 4.0 |
| Poly.toString() | 5.0 | 4.0 | 4.0 | 5.0 |
| Unit.mul(Unit) | 4.0 | 4.0 | 3.0 | 5.0 |
lexer 的 tokenNext()用于获取下一个 token,其中有大量判断结构,尽管我已经做过重构优化,但其控制结构复杂度还是难以控制。
parseFactor 倒是有优化余地,因为我对一些比较短的 Factor 类型的解析是直接放在 parseFactor 里的,可以拉出来单独一个方法。
note:
- CogC(全局复杂度):CogC 表示方法的复杂性,可以通过计算方法内的 McCabe 圈复杂度(Cyclomatic Complexity)来衡量。较高的 CogC 值表示方法的控制流程复杂,可能需要更多的测试和维护工作。
- ev(G)(边界边数):ev(G) 表示方法的边界边数,即方法内的边界路径个数。较高的 ev(G) 值表示方法中存在多个控制路径,可能导致更高的复杂性和测试工作量。
- iv(G)(内部边数):iv(G) 表示方法内部的边数,即方法内部控制结构之间的直接连接数。较高的 iv(G) 值表示方法内部控制结构之间的连接较多,可能导致更高的复杂性。
- v(G)(边界边数与内部边数之和):v(G) 表示方法内的控制结构总数,即 ev(G) 和 iv(G) 之和。较高的 v(G) 值表示方法内控制结构较多,可能导致更高的复杂性。
| module | v(G)avg | v(G)tot |
|---|---|---|
| project | 2.051546391752577 | 199.0 |

不算空行和注释, 848 行,似乎不算太多?
最长的是 Unit,这是因为我在 Unit 里放了太多优化和简化输出的内容了。
其次就是 Poly 了
上机训练万岁! hyggge,神!
上机教会了我:将指导书中要求的形式化表达,直接翻译成了一层层的递归下降的类
hyggge 的博客教会了我 Poly 和 Mono 这样的架构,实现各层级计算、输出的统一。
让我了解到后面会改基本项,所以提前将 Poly 和 Unit 的功能解耦。
基本项:coe*x^exp
新增自定义函数和指数函数。
很炸裂,忙了三天,我中测一个点也没过。
最后五六个小时,硬是被一个 exp(x)难住了,输出是 1,一直是 1.
调试的时候却出现了变化,竟然每次都不一样。
最后才发现,我的 toString 方法里使用并改变了未经深克隆的对象,导致调试的时候调用 toString 改变了对象,从而结果不一样
exp 这个只需要改一下基本项:coe*x^exp*e^poly
添加类 ExpFactor。
我这里还改了个类名,从 Mono 到 Unit
自定义函数定义:由 FunctionUtils 完成,内用 Parser 解析等号前的函数名和参数列表,并与函数定义式(字符串)一起放进一个 MyFunction 对象里。
自定义函数调用:ParseFactor 中解析实参列表和函数名,利用 FunctionUtils 里的方法进行虚参到实参的字符串替换,将替换后的字符串进行 Expr 解析,创建 FuncFactor 对象。
FuncFactor 的 toPoly 直接使用 Expr 的 toPoly 即可。
因为挂了,所以在 bug 修复的时候我有大把时间进行了一些重构。
第二次作业已经自动实现了,因为是虚参到实参的字符串替换,替换后进行 parseExpr,其中若有自定义函数调用,又会自动解析。
这次的上机训练的架构我本来也是想照抄的,但是在每个类中都写一个求导方法似乎并不符合我的初衷。我建立一个 Poly 和 Unit 类,就是为了能统一计算,方便管理。
所以我选择在 Poly 和 Unit 里直接求导。
基本项的导数是一个 Poly:coe*exp*x^(exp-1)*exp^poly+coe*x^exp*e^poly*dx(poly)
Poly 的导数是 units 中 unit 导数的和(利用 add()方法)。
比如添加求和函数,三角函数等,我就可以并且新增对应的函数 Factor,在基本项上添加对应的部分,在 Unit 的 mul()方法中添加一个新的 Factor 对应的 mul 方法。
尽管我实现了上述优化,但是复杂得难看。
第三次作业过程中我又将 Unit 的 toString 进行了简化。
分析未通过的公测用例和被互测发现的 bug:特征、问题所在的类和方法,为什么会出现这样的问题?你能否通过更好的设计避免这样的问题?
从某种意义上来说,我公测没错过————毕竟要么公测没错,要么中测没过。
毕竟 bug 修复时的数据点给了我很大启发。
在这里我还是重点分析我第二次挂的原因。
我在 Poly 的 toString()中,为了将正项先输出,首先将 units clone 了一下(问题就在这),然后遍历新的 units,有正项就输出,并将这个正项系数置 0,后续再输出剩余项。
然而 units(当时)是 Map,或者后来的 Set,clone 方法不是深克隆,增删没问题,改容器中的对象就不行。
最痛的事情,就是自己能测出 bug,却 de 不出来。尤其是每次输出结果不一样,让我很慌。
分析自己发现别人程序 bug 所采用的策略
一方面,我尝试卡边界,构造嵌套层数比较多的数据,但是很容易就超出了 cost 的要求。
另一方面,我利用自己的数据生成器和别人的评测机,进行大量测试.
我测出过好多次同学的 tle,实际上数据并不复杂,指数也在 6 那里,但是经过精简,cost 没问题了,程序确又能正常跑了。
所以到最后也没成功 hack 一次。
oopre中其实可以下放更多接口相关内容
| method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| DxFactor.DxFactor(Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
| DxFactor.toPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpFactor.clearExp() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpFactor.derive() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpFactor.ExpFactor(Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpFactor.toPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Expr.addTerm(Term) | 0.0 | 1.0 | 1.0 | 1.0 |
| Expr.derive() | 1.0 | 1.0 | 2.0 | 2.0 |
| Expr.Expr() | 0.0 | 1.0 | 1.0 | 1.0 |
| Expr.toPoly() | 1.0 | 1.0 | 2.0 | 2.0 |
| Expr.toString() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExprFactor.ExprFactor(Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
| ExprFactor.toPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factor.derive() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factor.Factor() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factor.getExp() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factor.setExp(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Factor.toPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factor.toString() | 0.0 | 1.0 | 1.0 | 1.0 |
| FuncFactor.FuncFactor(String, ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
| FuncFactor.toPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| FuncUtils.callFunction(String, ArrayList) | 6.0 | 3.0 | 5.0 | 5.0 |
| FuncUtils.defFunction(String) | 1.0 | 1.0 | 2.0 | 2.0 |
| FuncUtils.getFunction(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.curToken() | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.curTokenType() | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.getLeft() | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.getNumberToken() | 2.0 | 1.0 | 3.0 | 3.0 |
| Lexer.getPos() | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.Lexer(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| Lexer.tokenNext() | 16.0 | 7.0 | 8.0 | 15.0 |
| MainClass.main(String[]) | 1.0 | 1.0 | 2.0 | 2.0 |
| MyFunction.getDefinition() | 0.0 | 1.0 | 1.0 | 1.0 |
| MyFunction.getName() | 0.0 | 1.0 | 1.0 | 1.0 |
| MyFunction.getParams() | 0.0 | 1.0 | 1.0 | 1.0 |
| MyFunction.MyFunction(String, ArrayList, String) | 0.0 | 1.0 | 1.0 | 1.0 |
| NumberFactor.derive() | 0.0 | 1.0 | 1.0 | 1.0 |
| NumberFactor.NumberFactor(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| NumberFactor.toPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Parser.parseDxFactor() | 0.0 | 1.0 | 1.0 | 1.0 |
| Parser.parseExpr() | 4.0 | 1.0 | 3.0 | 4.0 |
| Parser.parseFactor() | 6.0 | 2.0 | 4.0 | 11.0 |
| Parser.parseFuncFactor() | 1.0 | 1.0 | 2.0 | 2.0 |
| Parser.Parser(Lexer) | 0.0 | 1.0 | 1.0 | 1.0 |
| Parser.parseSign() | 2.0 | 1.0 | 3.0 | 3.0 |
| Parser.parseTerm(int) | 1.0 | 1.0 | 2.0 | 2.0 |
| Poly.add(Poly) | 1.0 | 1.0 | 2.0 | 2.0 |
| Poly.addUnit(Unit) | 8.0 | 4.0 | 5.0 | 6.0 |
| Poly.clone() | 1.0 | 1.0 | 2.0 | 2.0 |
| Poly.compareTo(Poly) | 4.0 | 4.0 | 2.0 | 4.0 |
| Poly.derive() | 1.0 | 1.0 | 2.0 | 2.0 |
| Poly.equals(Object) | 3.0 | 3.0 | 2.0 | 4.0 |
| Poly.getUnits() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.hashCode() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.isFactor() | 1.0 | 1.0 | 2.0 | 2.0 |
| Poly.isZero() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.mul(Poly) | 3.0 | 1.0 | 3.0 | 3.0 |
| Poly.negate() | 1.0 | 1.0 | 2.0 | 2.0 |
| Poly.one() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.Poly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.Poly(TreeSet) | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.pow(BigInteger) | 3.0 | 1.0 | 3.0 | 3.0 |
| Poly.toString() | 5.0 | 4.0 | 4.0 | 5.0 |
| Poly.zero() | 0.0 | 1.0 | 1.0 | 1.0 |
| PowerFactor.derive() | 0.0 | 1.0 | 1.0 | 1.0 |
| PowerFactor.PowerFactor(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| PowerFactor.toPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| PreProcessor.mergeOperator(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| PreProcessor.preProcess(String) | 0.0 | 1.0 | 1.0 | 1.0 |
| PreProcessor.trimBlank(String) | 1.0 | 1.0 | 2.0 | 2.0 |
| Term.addFactor(Factor) | 1.0 | 1.0 | 2.0 | 2.0 |
| Term.derive() | 6.0 | 1.0 | 4.0 | 4.0 |
| Term.Term(int) | 0.0 | 1.0 | 1.0 | 1.0 |
| Term.toPoly() | 2.0 | 1.0 | 3.0 | 3.0 |
| Unit.add(Unit) | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.clone() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.compareTo(Unit) | 2.0 | 3.0 | 1.0 | 3.0 |
| Unit.couldAdd(Unit) | 1.0 | 1.0 | 2.0 | 2.0 |
| Unit.derive() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.derivePoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.derivePower() | 2.0 | 3.0 | 3.0 | 3.0 |
| Unit.equals(Object) | 4.0 | 3.0 | 4.0 | 6.0 |
| Unit.getCoeSign() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.getPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.getStrCount() | 3.0 | 1.0 | 1.0 | 4.0 |
| Unit.getStrings() | 8.0 | 1.0 | 6.0 | 6.0 |
| Unit.hashCode() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.isOne() | 2.0 | 1.0 | 4.0 | 4.0 |
| Unit.isZero() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.mul(Unit) | 4.0 | 4.0 | 3.0 | 5.0 |
| Unit.negate() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.one() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.onlyPoly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.onlyPower() | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.toString() | 9.0 | 2.0 | 4.0 | 6.0 |
| Unit.Unit(BigInteger, BigInteger, Poly) | 0.0 | 1.0 | 1.0 | 1.0 |
| Unit.zero() | 0.0 | 1.0 | 1.0 | 1.0 |
| Total | 118.0 | 127.0 | 167.0 | 199.0 |
| Average | 1.2164948453608246 | 1.309278350515464 | 1.7216494845360826 | 2.051546391752577 |