前言

这是面向对象课程的第三次作业总结博客。在第二次作业上进行了一定的迭代开发。工作量相比第二次作业而言并不大。

相较于第二次作业,本次作业实现的功能有:实现求导算子。

因为只需要实现一个功能,从任务量和难度来讲都是三次作业中最简单的一次。

整体架构

经过迭代后第三次作业的类图如下所示:

类图

本次迭代中涉及的工作有:

  • ExprTermVar 新增了求导方法(Expr 的求导方法处于 Tool 类中)。
  • Parser 新增了求导算子语法分析方法。
  • 对自定义函数的构造方法进行了一定的调整。
  • 对预处理函数进行了一些调整

对预处理的调整

主要调整是将 dx 替换为了 1*dx,这样可以保证解析到求导算子时处在 Term 的解析过程中。

求导实现思路

在遇到求导算子 dx/y/z 时,对其管辖的表达式进行语法分析。分析结束后对得到的表达式调用表达式求导函数得到求导后的表达式,后续处理与括号解析相同。

对于出现在自定义函数中的求导算子,我的处理是对自定义函数的表达式调用表达式语法分析,对表达式进行展开、化简操作,再进行形参替换。

Expr

对于 Expr 就是对其中每个 Term 都进行求导。采取循环即可,在循环过程中对每一项都进行求导。此3方法位于工具类 Tool 中。

Term

对于 Term 的求导主要考虑乘法法则。考虑到 (f(x)*g(x)*h(x))’=f(x)’*(g(x)*h(x))+f(x)*(g(x)*h(x))’=…,采用递归的方法解析。伪代码如下所示:

1
2
3
4
5
6
7
8
9
ArrayList<Term> termDerivative
new terms
if this.vars.size == 1 // 当只剩下一项的时候
add vars[0].derivative to terms // 返回原子项的求导结果
return terms
else
add mul(this.vars[0].derivative, this.vars[1,]) to terms // 前导后不导
add mul(this.vars[0], this.vars[1,].derivative) to terms // 前不导后导
return terms

Var

对于 Var 的求导,分为三种情况:

  1. 常数求导返回零
  2. 返回
  3. 三角函数返回三角函数求导后的值。

其中对三角函数的求导举例:

类复杂度分析

method CgoC ev iv v
Expr.equals(Expr) 16 5 5 9
Expr.Expr() 0 1 1 1
Expr.Expr(ArrayList) 2 2 2 3
Expr.getTerms() 0 1 1 1
Expr.toString() 6 3 5 5
Function.call(String) 9 1 6 7
Function.Function(String, ArrayList) 1 1 2 2
Function.getState() 0 1 1 1
Function.toString() 0 1 1 1
Lexer.getFunction() 5 1 2 4
Lexer.getNumber() 2 1 3 3
Lexer.Lexer(String) 0 1 1 1
Lexer.next() 9 2 6 8
Lexer.peek() 0 1 1 1
MainClass.main(String[]) 1 1 2 2
Parser.cket(ArrayList) 4 2 3 3
Parser.parseDerivation(ArrayList) 4 2 3 3
Parser.parseExpr() 2 1 3 3
Parser.parseFunction(ArrayList, String) 7 4 5 5
Parser.Parser(Lexer, ArrayList) 0 1 1 1
Parser.parseTerm() 14 1 11 11
Parser.parseVar() 23 7 11 15
Term.add(Term) 9 3 5 7
Term.addVar(Var) 1 1 2 2
Term.addVars(Term) 1 1 2 2
Term.copy() 0 1 1 1
Term.equals(Term) 17 6 5 10
Term.getConstant() 3 3 3 3
Term.getSymbol() 0 1 1 1
Term.setSymbol() 1 1 2 2
Term.setSymbol(int) 0 1 1 1
Term.similar(Term) 39 13 15 21
Term.simplify() 23 5 13 14
Term.Term() 0 1 1 1
Term.Term(int) 0 1 1 1
Term.Term(Var) 0 1 1 1
Term.termDerivative(Character) 6 3 4 4
Term.toString() 6 3 3 6
Tool.afterTreat(String) 3 1 6 6
Tool.exprDerivative(ArrayList, Character) 3 1 3 3
Tool.exprSimplify(ArrayList) 8 2 5 6
Tool.mul(ArrayList, ArrayList) 3 1 3 3
Tool.mul(ArrayList, Term) 1 1 2 2
Tool.mul(Term, Term) 0 1 1 1
Tool.preTreat(String) 2 1 5 5
Tool.removeSpace(String) 0 1 1 1
Var.addAbs(Var) 2 2 1 3
Var.copy() 0 1 1 1
Var.equals(Var) 5 4 3 6
Var.getCharacter() 0 1 1 1
Var.getExpr() 0 1 1 1
Var.getIndex() 0 1 1 1
Var.getNum() 1 1 2 2
Var.getSymbol() 0 1 1 1
Var.getType() 0 1 1 1
Var.setSymbol(int) 0 1 1 1
Var.simplify() 8 4 5 8
Var.subAbs(Var) 2 2 1 3
Var.toString() 11 6 7 10
Var.Var(int) 1 1 1 2
Var.Var(int, BigInteger, Character, int) 0 1 1 1
Var.Var(int, BigInteger, Character, int, int) 0 1 1 1
Var.Var(int, BigInteger, Character, int, int, ArrayList) 0 1 1 1
Var.Var(int, BigInteger, int, int) 0 1 1 1
Var.Var(int, Character, int, int) 0 1 1 1
Var.Var(int, int, int, ArrayList) 0 1 1 1
Var.varDerivative(Character) 15 3 6 8
Total 276 131 201 249
Average 4.119402985 1.955223881 3 3.71641791

复杂度分析

本次迭代后复杂度并没有额外的增加。整体上基本与第二次作业相同。

bug 分析

本次作业并没有发现 bug

hack 策略

整体上 hack 以跑评测机为主,手动构造数据为辅。

值得一提的是在互测过程中成功刀到了 TLEMLE。出刀的过程中包含了各种卡着代价上限的微调…结果是成功爆了一个人的时间与另一个人的堆,给我留下了珍贵的回忆。

总结

拜整体还算是优良的架构所赐,第三次作业的难度可谓是三次作业中最简单的。经验是在编程过程中,优良的架构设计是很重要的。在写出屎山代码时要勇于重构,不要尝试在屎山上添砖加瓦。

因为没有参加 OOpre,所以在写第一次作业的过程中遇到了莫大的困难,当时的心态也受到了莫大的影响。很希望课程组以后会改进这一点,要么不要取消寒假的 OOpre,要么把 OOpre 课程变成大二上的必修课。

另外是互测刀的很爽(逃),但是个人感觉代价的设定有点太严苛了,过于严苛的代价设定让本来是比较合理的 hack 都无法通过合法性检验,比较影响刀人体验。

第一单元的学习还是很有遗憾的,比如第一次作业强测直接寄的经历呜呜呜。

特别感谢 Tobysheep 对我学习的帮助。