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難度越小。
最後,感謝那些年跟我一塊兒分享測試代碼的小夥伴~~~