Poly是项,Proceed是处理类。git
第一次做业的时候,本身写了两天(debug)。只有两百行代码,耗时20+小时.....主要缘由在于罪恶的正则:正则表达式
正则是没法一次处理过长的式子的,一旦输入会爆出exception,而后一串串红红提示语......算法
引入循环,单项匹配框架
一次正则过长致使遗漏小括号以及与或非层次不对ide
将正则字符换命名,造成一个正则表达式的树结构,更易管理,逻辑性一目了然函数
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 }
语法分析:
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 }
很像一个计算器,设立了操做数栈与Derivinterface对象栈。每当读到+ - * sin cos ^时,都会将相应的对象栈和操做数栈(比较优先级后)中对象弹出。结合成一个新的Derivinterface对象后入栈。
一开始原本设计的时候但愿总体是一个Derivinterface对象,求导后依然是一个Derivinterface对象,这样便于作后续的优化。可是后面仍是求导直接将结果输出了,为了下降错误率,每一个加减组合项,嵌套组合项,能用到括号的地方本身还特地多加了括号,致使结果长度异常长。
加各类各样题目要求的细节了。比较磨人的环节是正负号的处理,很是地繁琐,本身每读一遍题目,就会发现本身又须要多考虑一点东西,这种细节很是多的设计,全是由于一开始设计时候,目光过于短浅。本身这一点设计习惯很是很差,老是依赖测试样例来验证本身的程序的正确性。可是测试样例总归是没法覆盖全部的特殊状况的。这个时候就须要逻辑上正确性证实本身程序。然而,因为coding期间,老是会东补补西凑凑,而后就把本身的结构的统一性破环掉了,这种状况在第三次做业的时候体现地尤其明显,本身对于一些特殊状况的处理,会放在一些奇奇怪怪的函数里面处理,“只要塞得下,就往里面塞”。最后,一个Term类里面装下了全部杂七杂八的判断。
第二次做业的复杂度最小。这一次也是本身coding时间最短的一次做业。由于没有不少地加各类各样的新的需求。说明了什么?
结构越有前瞻性,代码复杂度越小,coding难度越小。
最后,感谢那些年跟我一块儿分享测试代码的小伙伴~~~