咱們不生產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行之內。幾乎每次做業都會使一個類的結構異常地冗長。類的劃分還須要進一步地合理。
  • 關於代碼正確性測試。「寫代碼前就須要構造好測試樣例」,忽然發現老師上課時候講的這句話很是地有道理。這樣不只僅可讓咱們快速地熟悉題目並且不會讓咱們先入爲主地在寫完代碼後作無用的片面的測試。然而,不管怎樣構造測試樣例,都是一種過後的挽救行爲,在寫代碼的時候,在思考算法的時候,就應該對於本身的程序進行正確性證實。

 

最後,感謝那些年跟我一塊兒分享測試代碼的小夥伴~~~

相關文章
相關標籤/搜索