2026面向对象第一单元博客作业

王渊博-24371201 2026-03-30 10:53:29

一、基于度量来分析程序结构

ClassOCavgOCmaxWMC
Choice1.523
Derive112
Expfunc112
Expr1.45416
ExprFactor444
Func2.5515
Lexer4.671528
Mainclass555
Mono2.4636
Number113
Parser3.331120
Poly3.38744
Printer5.61184
Processor2.33614
Simplifier112
Term1.7537
Var1.523

 

MethodCogCev(G)iv(G)v(G)
Choice.Choice(Factor, Factor, Factor, Factor)0111
Choice.toPoly()1222
Derive.Derive(int, Expr)0111
Derive.toPoly()0111
Expfunc.Expfunc(Factor, BigInteger)0111
Expfunc.toPoly()0111
Expr.Expr()0111
Expr.addTerm(Term)0111
Expr.addoperator(String)0111
Expr.getExp()0111
Expr.getOperators()0111
Expr.getTerms()0111
Expr.setExp(BigInteger)0111
Expr.setOperators(ArrayList<String>)0111
Expr.setTerms(ArrayList<Term>)0111
Expr.toPoly()8244
Expr.toString()3133
ExprFactor.toPoly()8244
Func.ConverttoString()1222
Func.Func(String, String, String, String, String)0111
Func.getfactor(int)6135
Func.iterate()10177
Func.substitude(String, String)0111
Func.toPoly()0111
Lexer.Lexer(String)0111
Lexer.getNumber()2133
Lexer.getderive()5323
Lexer.getfuninput()7146
Lexer.next()234822
Lexer.peek()0111
Mainclass.main(String[])5135
Mono.Mono(BigInteger, BigInteger, BigInteger)0111
Mono.derivemono(int)3333
Mono.derivex()12688
Mono.derivey()12688
Mono.getCoe()0111
Mono.getExp()0111
Mono.getExppoly()0111
Mono.getEyp()0111
Mono.ismergeable(Mono)1761515
Mono.isonlynum()3245
Mono.monomultmono(Mono)8144
Mono.setCoe(BigInteger)0111
Mono.setExp(BigInteger)0111
Mono.setExppoly(Poly)0111
Mono.setEyp(BigInteger)0111
Number.Number(BigInteger)0111
Number.toPoly()0111
Number.toString()0111
Parser.Parser(Lexer, String, String, String, String)0111
Parser.parseChoice()0111
Parser.parseDerive()3123
Parser.parseExpr()2133
Parser.parseFactor()2081414
Parser.parseTerm()1122
Poly.addPoly(Poly)8455
Poly.clean(Poly)4144
Poly.copyPoly()3133
Poly.derivepoly(int)1122
Poly.getMonos()0111
Poly.isempty()3323
Poly.mulPoly(Poly)11288
Poly.onlysum(Poly)8455
Poly.polydivideBigintcoe(BigInteger)1122
Poly.polyequal(Poly)16768
Poly.pow(BigInteger)1122
Poly.setMonos(ArrayList<Mono>)0111
Poly.subPoly(Poly)1122
Printer.Print()127810
Printer.Printer(Poly)0111
Printer.Printexp(Poly)5155
Printer.Printexpwithjudgenone(int)2133
Printer.Printfirstcoenoone(int)2811212
Printer.Printfirstnegcoeone(int)2911212
Printer.Printfirstposcoeone(int)2911212
Printer.Printfirstpositive(int)4133
Printer.Printnegetive(int)2133
Printer.Printpositive(int)0111
Printer.alllen(Poly, BigInteger)3233
Printer.isexprfactor(Poly)1251213
Printer.isnumnoone(Poly)6378
Printer.mylen(String, boolean)8525
Printer.optimizeable(Poly)155910
Processor.Processor(String)0111
Processor.addzero(String)3233
Processor.process(String)0111
Processor.processadd(String)5177
Processor.processsignal(String)16178
Processor.processwhite(String)0111
Simplifier.Simplifier(Poly)0111
Simplifier.Simplify()0111
Term.Term()0111
Term.addFactor(Factor)0111
Term.toPoly()1122
Term.toString()3133
Var.Var(String, BigInteger)0111
Var.toPoly()2112

 其中,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和面向对象的深入理解,也渐渐有了对第二单元电梯难题的信心与能力。
未来第一单元的内容可以增添多变量函数,对表达式求高阶导数等。上课内容现在已可以较好地引入面向对象程序与设计。

...全文
40 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

309

社区成员

发帖
与我相关
我的任务
社区描述
2026年北航面向对象设计与构造
java 高校
社区管理员
  • 孙琦航
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧