309
社区成员
发帖
与我相关
我的任务
分享一、基于度量来分析程序结构
| Class | OCavg | OCmax | WMC |
| Choice | 1.5 | 2 | 3 |
| Derive | 1 | 1 | 2 |
| Expfunc | 1 | 1 | 2 |
| Expr | 1.45 | 4 | 16 |
| ExprFactor | 4 | 4 | 4 |
| Func | 2.5 | 5 | 15 |
| Lexer | 4.67 | 15 | 28 |
| Mainclass | 5 | 5 | 5 |
| Mono | 2.4 | 6 | 36 |
| Number | 1 | 1 | 3 |
| Parser | 3.33 | 11 | 20 |
| Poly | 3.38 | 7 | 44 |
| Printer | 5.6 | 11 | 84 |
| Processor | 2.33 | 6 | 14 |
| Simplifier | 1 | 1 | 2 |
| Term | 1.75 | 3 | 7 |
| Var | 1.5 | 2 | 3 |
| Method | CogC | ev(G) | iv(G) | v(G) |
| Choice.Choice(Factor, Factor, Factor, Factor) | 0 | 1 | 1 | 1 |
| Choice.toPoly() | 1 | 2 | 2 | 2 |
| Derive.Derive(int, Expr) | 0 | 1 | 1 | 1 |
| Derive.toPoly() | 0 | 1 | 1 | 1 |
| Expfunc.Expfunc(Factor, BigInteger) | 0 | 1 | 1 | 1 |
| Expfunc.toPoly() | 0 | 1 | 1 | 1 |
| Expr.Expr() | 0 | 1 | 1 | 1 |
| Expr.addTerm(Term) | 0 | 1 | 1 | 1 |
| Expr.addoperator(String) | 0 | 1 | 1 | 1 |
| Expr.getExp() | 0 | 1 | 1 | 1 |
| Expr.getOperators() | 0 | 1 | 1 | 1 |
| Expr.getTerms() | 0 | 1 | 1 | 1 |
| Expr.setExp(BigInteger) | 0 | 1 | 1 | 1 |
| Expr.setOperators(ArrayList<String>) | 0 | 1 | 1 | 1 |
| Expr.setTerms(ArrayList<Term>) | 0 | 1 | 1 | 1 |
| Expr.toPoly() | 8 | 2 | 4 | 4 |
| Expr.toString() | 3 | 1 | 3 | 3 |
| ExprFactor.toPoly() | 8 | 2 | 4 | 4 |
| Func.ConverttoString() | 1 | 2 | 2 | 2 |
| Func.Func(String, String, String, String, String) | 0 | 1 | 1 | 1 |
| Func.getfactor(int) | 6 | 1 | 3 | 5 |
| Func.iterate() | 10 | 1 | 7 | 7 |
| Func.substitude(String, String) | 0 | 1 | 1 | 1 |
| Func.toPoly() | 0 | 1 | 1 | 1 |
| Lexer.Lexer(String) | 0 | 1 | 1 | 1 |
| Lexer.getNumber() | 2 | 1 | 3 | 3 |
| Lexer.getderive() | 5 | 3 | 2 | 3 |
| Lexer.getfuninput() | 7 | 1 | 4 | 6 |
| Lexer.next() | 23 | 4 | 8 | 22 |
| Lexer.peek() | 0 | 1 | 1 | 1 |
| Mainclass.main(String[]) | 5 | 1 | 3 | 5 |
| Mono.Mono(BigInteger, BigInteger, BigInteger) | 0 | 1 | 1 | 1 |
| Mono.derivemono(int) | 3 | 3 | 3 | 3 |
| Mono.derivex() | 12 | 6 | 8 | 8 |
| Mono.derivey() | 12 | 6 | 8 | 8 |
| Mono.getCoe() | 0 | 1 | 1 | 1 |
| Mono.getExp() | 0 | 1 | 1 | 1 |
| Mono.getExppoly() | 0 | 1 | 1 | 1 |
| Mono.getEyp() | 0 | 1 | 1 | 1 |
| Mono.ismergeable(Mono) | 17 | 6 | 15 | 15 |
| Mono.isonlynum() | 3 | 2 | 4 | 5 |
| Mono.monomultmono(Mono) | 8 | 1 | 4 | 4 |
| Mono.setCoe(BigInteger) | 0 | 1 | 1 | 1 |
| Mono.setExp(BigInteger) | 0 | 1 | 1 | 1 |
| Mono.setExppoly(Poly) | 0 | 1 | 1 | 1 |
| Mono.setEyp(BigInteger) | 0 | 1 | 1 | 1 |
| Number.Number(BigInteger) | 0 | 1 | 1 | 1 |
| Number.toPoly() | 0 | 1 | 1 | 1 |
| Number.toString() | 0 | 1 | 1 | 1 |
| Parser.Parser(Lexer, String, String, String, String) | 0 | 1 | 1 | 1 |
| Parser.parseChoice() | 0 | 1 | 1 | 1 |
| Parser.parseDerive() | 3 | 1 | 2 | 3 |
| Parser.parseExpr() | 2 | 1 | 3 | 3 |
| Parser.parseFactor() | 20 | 8 | 14 | 14 |
| Parser.parseTerm() | 1 | 1 | 2 | 2 |
| Poly.addPoly(Poly) | 8 | 4 | 5 | 5 |
| Poly.clean(Poly) | 4 | 1 | 4 | 4 |
| Poly.copyPoly() | 3 | 1 | 3 | 3 |
| Poly.derivepoly(int) | 1 | 1 | 2 | 2 |
| Poly.getMonos() | 0 | 1 | 1 | 1 |
| Poly.isempty() | 3 | 3 | 2 | 3 |
| Poly.mulPoly(Poly) | 11 | 2 | 8 | 8 |
| Poly.onlysum(Poly) | 8 | 4 | 5 | 5 |
| Poly.polydivideBigintcoe(BigInteger) | 1 | 1 | 2 | 2 |
| Poly.polyequal(Poly) | 16 | 7 | 6 | 8 |
| Poly.pow(BigInteger) | 1 | 1 | 2 | 2 |
| Poly.setMonos(ArrayList<Mono>) | 0 | 1 | 1 | 1 |
| Poly.subPoly(Poly) | 1 | 1 | 2 | 2 |
| Printer.Print() | 12 | 7 | 8 | 10 |
| Printer.Printer(Poly) | 0 | 1 | 1 | 1 |
| Printer.Printexp(Poly) | 5 | 1 | 5 | 5 |
| Printer.Printexpwithjudgenone(int) | 2 | 1 | 3 | 3 |
| Printer.Printfirstcoenoone(int) | 28 | 1 | 12 | 12 |
| Printer.Printfirstnegcoeone(int) | 29 | 1 | 12 | 12 |
| Printer.Printfirstposcoeone(int) | 29 | 1 | 12 | 12 |
| Printer.Printfirstpositive(int) | 4 | 1 | 3 | 3 |
| Printer.Printnegetive(int) | 2 | 1 | 3 | 3 |
| Printer.Printpositive(int) | 0 | 1 | 1 | 1 |
| Printer.alllen(Poly, BigInteger) | 3 | 2 | 3 | 3 |
| Printer.isexprfactor(Poly) | 12 | 5 | 12 | 13 |
| Printer.isnumnoone(Poly) | 6 | 3 | 7 | 8 |
| Printer.mylen(String, boolean) | 8 | 5 | 2 | 5 |
| Printer.optimizeable(Poly) | 15 | 5 | 9 | 10 |
| Processor.Processor(String) | 0 | 1 | 1 | 1 |
| Processor.addzero(String) | 3 | 2 | 3 | 3 |
| Processor.process(String) | 0 | 1 | 1 | 1 |
| Processor.processadd(String) | 5 | 1 | 7 | 7 |
| Processor.processsignal(String) | 16 | 1 | 7 | 8 |
| Processor.processwhite(String) | 0 | 1 | 1 | 1 |
| Simplifier.Simplifier(Poly) | 0 | 1 | 1 | 1 |
| Simplifier.Simplify() | 0 | 1 | 1 | 1 |
| Term.Term() | 0 | 1 | 1 | 1 |
| Term.addFactor(Factor) | 0 | 1 | 1 | 1 |
| Term.toPoly() | 1 | 1 | 2 | 2 |
| Term.toString() | 3 | 1 | 3 | 3 |
| Var.Var(String, BigInteger) | 0 | 1 | 1 | 1 |
| Var.toPoly() | 2 | 1 | 1 | 2 |

其中,Processor进行预处理,Lexer、Parser词法解析得到字符流,实现Factor接口的众多类表示各种因子、项和表达式,Mono和Poly代表单项式多项式,为最后的保存形式,Printer类负责优化(提公因数等)和输出。
二
很幸运,在三次作业的迭代过程中,我的架构具有一定的可扩展性,而且在时间空间等性能上也不是很差,因此没有进行重构。个人认为第一次作业的构建过程最为复杂,在翻阅了很多学长的博客之后,我费力的理解了parse的过程,最后选择了Poly-Mono架构,用toPoly转为poly(Arraylist<Mono>)进行储存,但我并没有用常见的toString方法转为字符串后输出,而是写了Printer类直接负责输出。本次作业初步构建起了表达式化简的底层逻辑,lexer和parser得到Factors,用toPoly实现factor到poly的转换,得到a*x^b的累加,最后由Printer输出。
第二次作业中加了指数函数、表达式因子和函数调用,直接在parser中添加对应因子的处理方法即可。指数函数的添加使得底层架构变为a*x^b*exp(exppoly)的累加,此处要注意exppoly要设置一个“零值”(如NULL),否则会形成无限递归。表达式因子需要注意短路处理,函数调用的处理方式比较多,流传较广的方法主要有两个:预处理时字符串替换、Poly或Mono的替换。显然字符串替换是更容易理解的,然而如果预处理时替换会导致一些问题,首当其冲的就是选择表达式中被短路factor的极高替换复杂度问题难以解决。而我又不愿意选择第二种实现起来更复杂的方式,因此我(创新?)用了第三种方式——后处理时进行字符串替换。即不在预处理时替换字符串,而是在toPoly时如果遇到函数调用,此时再进行字符串替换,然而现在得到的是一个字符串,所以需要再次调用lexer和parser进行分解得到expr,然后再调用expr.toPoly变为Poly,这样就可以巧妙处理短路的问题。
第三次作业新增了递归函数,变量y和求导。递归函数依然沿用普通函数类,只是在函数类内新加了六个递归函数的表达式处理(还是后处理,即func.toPoly时预先进行五次字符串替换得到f{0}到f{5}的字符串形式)。变量y的添加使得基本结构变为a*x^b*y^c*exp(exppoly),只是在输出Printer时新增了很多讨论情况。求导的实现较为简单,只需要在Poly类和Mono类中添加求导方法即可,最后化为a*x^b*y^c*exp(exppoly)的求导,而grad就是dx+dy。
新的迭代场景:假如有函数g、h等扩展,也可以很简单的直接在Func类里添加即可,不必再添加别的类。
三
三次测评中,只有第三次作业中有一个bug,导致进了c房,而错误竟然是递归函数定义时f{0}和f{1}的位置不确定(当时没看到),恰好自己的测评机也没有测这个地方(幸亏没有因此进不了互测)。修改起来也很简单,只需要将第一行按“=”分割取第一项,用string.contains(“1”)即可判断是否需要交换,如果需要交换直接在func因子初始化时交换传参即可。
四
检测别人代码bug无非就是几个角度,除了正确性测试以外(主要是测评机),是否去掉了必要括号,选择式短路,压力测试等几个角度是常错点,当然还有f{0}和f{1}的位置不确定这种特殊错误(不只是我自己犯了这个错)。
五
第一次的优化很简单,保证第一项输出是正的即可(如果有)。第二次优化难度极大,我只实现了较为简单的exppoly提最短公因数,即提公因数除1到9的商,并没有实现exp的拆添项。第三次优化同第二次。
优化最重要的两点在于保证正确,同时不过度影响性能。即如果给出一个实际上根本不需要优化的式子,在运行完优化方法之后,运行时间应当几乎不变,即充分考虑各种情况尽快跳出(如gcd为1直接跳出,又如只比较各个mono的系数的长度和,而不必将整个poly转成字符串再在统计长度,很多人在此次的时间性能降到极低)。
六
我在作业中几乎没有使用ai,只是在某些string和正则表达式的语法上进行了查阅(如contains、split、index等),其余部分没用过ai。互测房间中没有关注是否存在ai生成的代码。
抽象是提供了概括共性特征的能力——经过一个单元的三次迭代,我对面向对象有了更深入的理解,第一单元通过递归下降与oopre最后一次作业衔接,经过三次迭代,逐渐加深对java和面向对象的深入理解,也渐渐有了对第二单元电梯难题的信心与能力。
未来第一单元的内容可以增添多变量函数,对表达式求高阶导数等。上课内容现在已可以较好地引入面向对象程序与设计。