咱们不生产bug,咱们只是算法的搬运工——OO第一次做业(踩雷)总结

第一次做业

类图:

解释:

  Poly是项,Proceed是处理类。git

 

第一次做业的时候,本身写了两天(debug)。只有两百行代码,耗时20+小时.....主要缘由在于罪恶的正则:正则表达式

  • 坑点1:

正则是没法一次处理过长的式子的,一旦输入会爆出exception,而后一串串红红提示语......算法

  • 解决方法:

引入循环,单项匹配框架

  • 坑点2:

一次正则过长致使遗漏小括号以及与或非层次不对ide

  • 解决方法:

将正则字符换命名,造成一个正则表达式的树结构,更易管理,逻辑性一目了然函数

 

 重大问题:

  • 从上图很明显地看出,inputanalysis函数内语句过多,本身把功能过于集中地写在了一个类当中。

 

第二次做业

——引入sincos

类图:

解释:

Controller是核心处理类;测试

Factor是因子,按照正负号分割。Term是项,按照乘除号分割。优化

 

 

第三次做业:

(为了想出来一个框架花了整整一个晚上.......)spa

类图:

 

结构:

其中,是按照指导书的推荐作法,将全部的项分为了所有的6个类:debug

类名 项的属性 做用
Num 基本项 简单地描述常数项
Mi 基本项 描述幂指数项
Sincos 基本项 描述三角函数项
Addsub 组合项 left 与 right属性分别是一个Derivinterface对象,表示加减组合项
Muldiv 组合项 left 与 right属性分别是一个Derivinterface对象,表示乘法组合项
Qiantao 组合项 为一个嵌套函数,因为幂函数不支持表达式,外层函数必定为三角函数,记录外层三角函数的类型与幂次。同时含有right属性是一个Derivinterface对象,表示内函数

同时加上一个处理全部输入输出,建立对象的Term类,与Derivinterface接口(抽象类)。

最关键的实属Derivinterface抽象类。上面的6种跟项有关的类,都实现了这个接口。咱们把输入的一整个表达式都处理成一个Derivinterface抽象类。

算法:

——彻底的编译思想

甚至还写了词法分析与语法分析模块。

词法分析:

 1     //词法分析部分
 2     public static void split(String raw, Deque<String> deque) {
 3         int i = 0;
 4         while (i < raw.length()) {
 5             if (raw.charAt(i) == '+' || raw.charAt(i) == '-') {
 6                 i = copyAddminus(raw, i, deque);
 7             } else if (raw.charAt(i) == 's' || raw.charAt(i) == 'c') {
 8                 i = copySincos(raw, i, deque);
 9             } else if (raw.charAt(i) == 'x') {
10                 i = copyX(raw, i, deque);
11             } else if (raw.charAt(i) >= '0' && raw.charAt(i) <= '9') {
12                 i = copyDigit(raw, i, deque);
13             } else if (raw.charAt(i) == '(' || raw.charAt(i) == ')') {
14                 deque.addLast(String.valueOf(raw.charAt(i)));
15                 i++;
16             } else if (raw.charAt(i) == '*') {
17                 deque.addLast(String.valueOf(raw.charAt(i)));
18                 i++;
19             } else if (raw.charAt(i) == '^') {
20                 i = copyIndex(raw, i, deque);
21             } else {
22                 //由于表达式是不能够有幂运算的,因此^字符只可能出如今x,sin cos当中
23                 Term.error();
24             }
25         }
26     }
View Code

  语法分析:

 1  //语法分析部分
 2     public static DerivInterface grammar(Deque<String> deque) {
 3         Stack<DerivInterface> termStack = new Stack<DerivInterface>();
 4         Stack<String> opStack = new Stack<String>();
 5         String head = null;
 6         while (deque.isEmpty() == false) {
 7             head = deque.pollFirst();
 8             if (isop(head) == true) {
 9                 if (termStack.isEmpty() == false) {
10                     if (canPush(head) == true) {
11                         opStack.push(head);
12                     } else if (head.equals("*")) {
13                         mul(head, termStack, opStack);
14                     } else if (head.equals("+") || head.equals("-")) {
15                         add(head, termStack, opStack);
16                     } else if (head.equals(")")) {
17                         pop(head, termStack, opStack);
18                     }
19                 } else {
20                     opStack.push(head);
21                 }
22             } else {
23                 termStack.add(newSimpleTerm(head));
24             }
25         }
26         while (opStack.isEmpty() == false) {
27             opEnd(termStack, opStack);
28         }
29         //最后应该Dequeue和opStack都为空而且termStack只有一个元素
30         if (opStack.isEmpty() == false || termStack.size() != 1) {
31             Term.error();
32         }
33         return termStack.pop();
34     }
View Code

  很像一个计算器,设立了操做数栈与Derivinterface对象栈。每当读到+ - *  sin cos ^时,都会将相应的对象栈和操做数栈(比较优先级后)中对象弹出。结合成一个新的Derivinterface对象后入栈。

为何答案那么长?

  一开始原本设计的时候但愿总体是一个Derivinterface对象,求导后依然是一个Derivinterface对象,这样便于作后续的优化。可是后面仍是求导直接将结果输出了,为了下降错误率,每一个加减组合项,嵌套组合项,能用到括号的地方本身还特地多加了括号,致使结果长度异常长。

时间都去哪儿啦?

  加各类各样题目要求的细节了。比较磨人的环节是正负号的处理,很是地繁琐,本身每读一遍题目,就会发现本身又须要多考虑一点东西,这种细节很是多的设计,全是由于一开始设计时候,目光过于短浅。本身这一点设计习惯很是很差,老是依赖测试样例来验证本身的程序的正确性。可是测试样例总归是没法覆盖全部的特殊状况的。这个时候就须要逻辑上正确性证实本身程序。然而,因为coding期间,老是会东补补西凑凑,而后就把本身的结构的统一性破环掉了,这种状况在第三次做业的时候体现地尤其明显,本身对于一些特殊状况的处理,会放在一些奇奇怪怪的函数里面处理,“只要塞得下,就往里面塞”。最后,一个Term类里面装下了全部杂七杂八的判断。

 

 

三次做业复杂度比较:

 

第一次:

第二次:

第三次:

 

analysis:

  第二次做业的复杂度最小。这一次也是本身coding时间最短的一次做业。由于没有不少地加各类各样的新的需求。说明了什么?

  结构越有前瞻性,代码复杂度越小,coding难度越小。

Drawbacks:

 

  • 第三次做业,几乎全部的功能都集中到了term类上面。本身的Term类异常冗长,本身删除了很久的代码才在500行之内。几乎每次做业都会使一个类的结构异常地冗长。类的划分还须要进一步地合理。
  • 关于代码正确性测试。“写代码前就须要构造好测试样例”,忽然发现老师上课时候讲的这句话很是地有道理。这样不只仅可让咱们快速地熟悉题目并且不会让咱们先入为主地在写完代码后作无用的片面的测试。然而,不管怎样构造测试样例,都是一种过后的挽救行为,在写代码的时候,在思考算法的时候,就应该对于本身的程序进行正确性证实。

 

最后,感谢那些年跟我一块儿分享测试代码的小伙伴~~~

相关文章
相关标签/搜索