1. 程式人生 > 其它 >OO Unit1 Expression Simplification

OO Unit1 Expression Simplification

OO第一單元總結,本文將從以下四個部分展開:架構設計、完備測試、bug與hack、優化策略

設計

本單元三次作業我都採用了為優化服務、為計算服務的架構,採用這樣架構的原因是:最後的輸出實際上只是對原表示式的資料運算結果進行輸出,不涉及原輸入的文法的儲存,因此完全略去了對儲存的需求,採用邊解析邊計算的策略。

這樣的架構優勢在於:

  • 只涉及對一個儲存單元的運算,只需要實現對儲存單元的運算即可,運算結構相對統一

  • 為優化服務,優化只涉及儲存單元內部的運算

  • 頂層結構簡單,解析部分外只有計算類

這樣簡化的結構也帶來一些問題:

  • 可擴充套件性較差,每次作業都只是當前需求的區域性較優解,很難適應新的需求

  • 儲存結構上耦合度過高,導致需求變化後儲存結構都面臨重構

  • 繼承關係較少,抽象層次不夠清晰

hw1

第一次作業難度較低,主要體現了整體設計上的方法:遞迴下降法。這一文法分析策略適用於上下文無關文法,也就是LL文法。我們的文法實際上是一種一元相關的Markov chain,每一個詞法單元唯一確定了後面的詞法結構,因此只需要完成對各個語法結構的解析即可。句法結構的下降體現於此,遞迴則體現在括號,括號內的層次又作為了高層次結構,需要再次遞迴,對括號內句法進行分析。規定了括號層數,實際上規定了遞迴表示式的遞迴樹深度不能超過1。

設計上我只採用了一個Polynomial儲存層次結構,記憶體若干個BigInteger對錶示x的係數與對應的指數,結構過於簡單,不做展示與分析

hw2

第二次作業難度驟增,為了更好體現工廠模式,我對解析過程進行了重構

  • 工廠模式:解析層次上,設定了一個工廠類產生Factor,可以作為函式引數的類實現getParameter

  • 優化導向:儲存層次上,設定三個層次結構,優化有關的層次結構集中於MetaData一個部分,也就是相同x指數對應的三角多項式(Trigonometric polynomial)內部進行合併

這樣的設計實際上有一定的冗餘度,而且並沒有在儲存層次上進行分割與抽離,只是從結構上看起來更像OOP了一點,並沒有實質上將不同物件的特徵進行提取

UML類圖如下,這個圖中可以很清晰的看到遞迴下降的過程。

 

儲存層次結構的複雜度如下:

 

可以看到,把優化的任務集中在一個類上,導致了優化相關的部分(sinMergeable、Mergeable、simplify)等複雜度都過高了,這一點為後面出現bug與debug都埋下了一定的隱患。

hw3

第三次作業雖然需求上完全可以在第二次的基礎上增量開發,但我感受到了極簡主義的召喚,以及預估了原結構優化的難度之大,故進行了整體重構。

我將所有解析的過程重新歸於Parser處理,同樣採用解析的同時計算的策略,同時為了一般化處理,我將三角函式內部視為一個多項式進行儲存,比較時比較二者差異即可

這次的重構在我看來充滿爭議性,優勢在於:

  • 簡化了不必要的類,解析任務集中統一化,避免了套用工廠模式之嫌(化作了抽象工廠,更簡單啦)

  • 儲存層次結構使用HashMap實現,找key的過程更簡化,省去了無用而繁瑣的維持有序的過程

問題在於:

  • 因為是最後一次單元,故沒有考慮可擴充套件性

  • parser表示壓力山大

UML類圖如下:

 

複雜度分析:

 

 

本次作業我做了勾股定理(Pythagorean theorem),優化asin(x)**2+bcos(x)**2與二倍角(Double angle),優化2**n*sin(x)**n*cos(x)**n

上圖中複雜度較高的部分都是我用於化簡的部分,因此帶來了較高的複雜度我認為是難以避免的。同時我設計了全域性優化開關,可以通過開關控制是否優化,因此若是優化部分出鍋可以通過關閉全域性優化開關簡單解決

測試

本單元三次作業我採用了兩種不同的測試方案。

隨機生成普通資料

第一次作業特殊情況與優化較少,邊緣資料使用隨機生成的資料可以達到較好的覆蓋,因此我使用java內建隨機數生成隨機生成資料進行測試。隨機數生成的過程實際就是解析的逆過程,可以稱之為“遞迴上升法”句法構造,生成的資料也可以通過引數的設定完成customerize。

//隨機資料生成
import java.util.Random;

public class Generater {
   public static final int MAX_RECURSION_DEPTH 1;
   public static final int MAX_FACTOR_COUNT 100;
   public static final int MAX_POLY_INDEX 20;
   public static final int MAX_POWER_INDEX 20;
   private static final int MAX_NUM_LEN 5;
   private Random random;

   public Generater() { this.random new Random();}

   //最長長度與長度等級
   public String generatePoly(int maxLen, int lenLvl, int curdepth) {
       StringBuilder sb new StringBuilder();
       int random.nextInt();
       if (== 0) { sb.append('+'); }
       else if (== 1) { sb.append('-'); }
       sb.append(generateTerm(maxLen 3, lenLvl 1, curdepth));
       String generateTerm(maxLen 3, lenLvl 1, curdepth);
       // 1/EPLL probability of termination each time
       while (sb.length() s.length() maxLen && lenLvl != 0) {
           if (== 0) { sb.append('+'); }
           else { sb.append('-'); }
           sb.append(s);
           generateTerm(maxLen 3, lenLvl 1, curdepth);
      }
       return sb.toString();
  }

   public String generateTerm(int maxLen, int lenLvl, int curdepth) {
       StringBuilder sb new StringBuilder();
       int random.nextInt();
       if (== 0) { sb.append('+'); }
       else if (== 1) { sb.append('-'); }
       sb.