BUAA_OO 第一單元總結 表示式解析與化簡
程式結構分析
HW1
資料的組織基本上都是自己一拍腦袋亂想的,沒有經過深思熟慮。做這個作業的時候,我的思維還停留在“過了就行,下次重構下次再說”這樣……
解析方法參考了第一次實驗課的程式碼。
程式碼規模
類的名稱 | 屬性個數 | 方法個數 | 行數 |
---|---|---|---|
Main | 2 | 6 | 146 |
Number | 2 | 3 | 18 |
Term | 2 | 7 | 65 |
Sum | 2 | 6 | 40 |
Pow | 2 | 2 | 25 |
其實這次作業寫的程式碼挺少的,但是功能也很有限
僅僅實現了最基礎的展開,合併也是在所有展開做完之後進行的。所以算個$(x-x)**1000$都得算半天
真是愚蠢的程式碼!
UML圖
嘗試用IDEA生成了個uml圖,**(髒話)這是啥?太醜了吧!其實這就表明我在HW1的架構特別爛
這是手畫的UML圖:
在這次作業中,我用來組織資料的物件只有Term(多項式的項),Sum(項的和),Number(Term的子類)
可改進的地方:
- 從這個UML圖可以看出來,這個叫Pow的類被晾在了一邊,其實這個玩意只是封裝了一個我用來展開指數括號的方法,完全沒必要作為一個類。把這個方法放在Term類裡面更合適
- Main類太雜亂。由於缺乏經驗與審美,我把一堆用來解析表示式的方法都塞進了Main裡面。這塊應該單獨整一個Parser類,把獲取輸入和解析輸入分離。
HW2
在HW2中就改進了很多問題(基本上等於Remake)
- 在研討課之後改了自己混亂的架構,對常規的思路進行了模仿
- 程式碼寫的更像程式碼了
程式碼規模
類的名稱 | 屬性個數 | 方法個數 | 行數 |
---|---|---|---|
middle.Function | 3 | 2 | 57 |
middle.Parser | 1 | 8 | 216 |
middle.Pow | 3 | 3 | 34 |
middle.Sum | 1 | 2 | 27 |
organize.factor.Number | 1 | 6 | 40 |
organize.factor.PowerFactor | 2 | 5 | 43 |
organize.factor.SinOrCos | 3 | 8 | 65 |
organize.Expr | 1 | 7 | 117 |
organize.Term | 2 | 9 | 141 |
Main | 0 | 2 | 34 |
這麼一整,明顯感覺程式清晰多了(起碼拿給別人看的時候不覺得丟人
UML圖
不算很醜:):)
這是整個專案的結構:
src
├── NormalMain.java//主函式
├── middle//用於進行中間過程的計算
│ ├── Function.java
│ ├── Parser.java
│ ├── Pow.java
│ └── Sum.java
└── organize//資料的組織
├── Expr.java
├── Term.java
└── factor//因子
├── BasicFactor.java//這是一個介面
├── PowerFactor.java
└── SinOrCos.java
HW3
程式碼規模
類的名稱 | 屬性個數 | 方法個數 | 行數 |
---|---|---|---|
middle.Function | 3 | 2 | 76 |
middle.Parser | 1 | 7 | 197 |
middle.Pow | 3 | 3 | 34 |
middle.Sum | 1 | 2 | 27 |
organize.factor.Number | 1 | 6 | 40 |
organize.factor.PowerFactor | 2 | 5 | 49 |
organize.factor.SinOrCos | 3 | 8 | 76 |
organize.Expr | 1 | 7 | 162 |
organize.Term | 2 | 9 | 148 |
Main | 0 | 2 | 33 |
圈複雜度分析
可以看出來,parser方法圈複雜度非常高,另外還有Term的Simplify方法,而Term的Simplify也正是我出bug的地方之一。
總體上來看,圈複雜度相當高的地方還不少,我應該之後寫程式碼的時候多注意控制,儘量減少方法之間的耦合。
Method | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
middle.Function.Function(String) | 3.0 | 2.0 | 3.0 | 4.0 |
middle.Function.substitution(String) | 10.0 | 1.0 | 9.0 | 9.0 |
middle.Parser.countNeg(String) | 4.0 | 4.0 | 3.0 | 4.0 |
middle.Parser.mulOutBrackets(String) | 8.0 | 3.0 | 6.0 | 9.0 |
middle.Parser.parse2(String) | 15.0 | 10.0 | 9.0 | 11.0 |
middle.Parser.parser(String) | 29.0 | 8.0 | 9.0 | 11.0 |
middle.Parser.pmOutBrackets(String) | 13.0 | 3.0 | 8.0 | 11.0 |
middle.Parser.powOutBrackets(String) | 8.0 | 3.0 | 5.0 | 8.0 |
middle.Parser.putFun(String, Function) | 0.0 | 1.0 | 1.0 | 1.0 |
middle.Pow.getExpr() | 0.0 | 1.0 | 1.0 | 1.0 |
middle.Pow.Pow(Expr, int) | 0.0 | 1.0 | 1.0 | 1.0 |
middle.Pow.standardize() | 2.0 | 2.0 | 2.0 | 3.0 |
middle.Sum.getExpr() | 0.0 | 1.0 | 1.0 | 1.0 |
middle.Sum.Sum(String, String, String, String) | 1.0 | 1.0 | 2.0 | 2.0 |
NormalMain.main(String[]) | 0.0 | 1.0 | 1.0 | 1.0 |
NormalMain.run() | 1.0 | 1.0 | 2.0 | 2.0 |
organize.Expr.add(Expr) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Expr.equals(Object) | 9.0 | 3.0 | 6.0 | 8.0 |
organize.Expr.Expr(ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Expr.Expr(BasicFactor) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Expr.Expr(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Expr.Expr(char) | 1.0 | 1.0 | 2.0 | 2.0 |
organize.Expr.hashCode() | 1.0 | 1.0 | 2.0 | 2.0 |
organize.Expr.isBasic() | 2.0 | 3.0 | 1.0 | 3.0 |
organize.Expr.neg() | 1.0 | 1.0 | 2.0 | 2.0 |
organize.Expr.simpilize() | 7.0 | 1.0 | 5.0 | 5.0 |
organize.Expr.times(Expr) | 8.0 | 3.0 | 5.0 | 5.0 |
organize.Expr.toString() | 6.0 | 2.0 | 4.0 | 5.0 |
organize.factor.PowerFactor.equals(Object) | 4.0 | 3.0 | 2.0 | 5.0 |
organize.factor.PowerFactor.getPower() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.PowerFactor.hashCode() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.PowerFactor.isBasic() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.PowerFactor.PowerFactor(long) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.PowerFactor.toString() | 2.0 | 3.0 | 1.0 | 3.0 |
organize.factor.SinOrCos.copy() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.equals(Object) | 4.0 | 3.0 | 4.0 | 6.0 |
organize.factor.SinOrCos.getParameter() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.getPower() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.hashCode() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.isBasic() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.isSin() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.setPower(long) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.SinOrCos(Expr, long, boolean) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.factor.SinOrCos.toString() | 3.0 | 1.0 | 1.0 | 4.0 |
organize.factor.Variable.Variable(String) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.equals(Object) | 3.0 | 3.0 | 2.0 | 4.0 |
organize.Term.getCoefficient() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.getFactors() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.hashCode() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.isBasic() | 4.0 | 3.0 | 3.0 | 5.0 |
organize.Term.neg() | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.setCoefficient(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.simplify() | 22.0 | 2.0 | 9.0 | 10.0 |
organize.Term.Term(BasicFactor) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.Term(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.Term(BigInteger, ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.Term(char) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.times(Term) | 0.0 | 1.0 | 1.0 | 1.0 |
organize.Term.toString() | 2.0 | 2.0 | 2.0 | 2.0 |
organize.Term.toStringNoSign() | 5.0 | 2.0 | 3.0 | 5.0 |
Total | 178.0 | 107.0 | 144.0 | 182.0 |
Average | 2.966666666666667 | 1.7833333333333334 | 2.4 | 3.033333333333333 |
UML圖
hw3真的和hw2沒什麼區別,UML如下,加了一根線而已
Bug及測試
自己的bug
第一次沒被hack。
第二次由於粗心(並且測試資料不完備)導致把sin(0)化簡為了1,直到交上去都沒有發現。
第三次bug有點多
- 首先是sum求和上下界我用的是long,碰到較大的資料就不行了-->
舉一反三
- sum求和使用字串替換處理,負數要加括號-->
仍然要舉一反三
- 另一個bug完全是由於我最後臨提交做了一點改動導致輸出邏輯出了點問題還沒做測試-->
任何時候都要測試,不要盲目自信
從這幾次作業中獲得的小小經驗在下面說吧。
hack策略
hack別人本質上也就是測試,那就對拍唄,寫個指令碼,跑起來
表示式化簡正確性的判定可以代入幾個x判斷。
關鍵點在於資料的生成。(見下文)
測試經驗
-
儲存資料,迴歸測試
不要小瞧一些很簡單的資料(其實出錯的往往是一些簡單的資料)
把自己和別人有過問題的資料都儲存起來,每次改版都重新做測試。
-
構造邊界資料
資料生成器指令碼生成的資料的特點是隨機,隨機就意味著不能百分之百覆蓋到所有可能的型別,手動構造更有目的性,殺傷力更強。
-
關注隨機生成資料的“完備性”,而不只是盲目測很多次
心得體會
HW1-閉門造車
閉門造車就是賭
習慣於寫完作業再去對答案,還是目光過於狹隘。思維被限制在“最正確的答案只有一個”裡面,難以出來。
這是有骨氣還是目光短淺呢?
語文不好,表達不盡意思,最後只剩下一句:“博採眾長”是褒義詞
HW2-"抄作業"
在研討課上聽到別人的思路,感覺真清晰,拿來用了。
HW3-感覺還行
在做HW2的時候比較關注可拓展性,又因為作業三相比2變化不大,很快就寫完了。
總體評價
其實要我評價我的這三次作業,答案是:都不滿意。
其實作業三交上去的程式碼有不少地方我還想重構一次
最主要的一點是Term和Expr的組織:
我覺得用HashMap存放Term中的因子和Expr裡面的Term更合適
BasicFactor不要包含次數的資訊,想三角函式這種就只儲存sin()括號裡面的東西
Term不要關注coefficient(常係數),僅僅儲存冪函式、三角函式,其中的項儲存為
HashMap<BasicFactor,Integer> factors;//Factor是次數
Expr用一個HashMap儲存Term和其前面的常係數的關係
HashMap<Term,Integer> terms;
這樣處理起來就更方便(起碼比原來會好一些
其實用HashMap存放這個在自然不過了,而我,雖然早就想到了,但是一方面由於定勢思維,一方面又由於寫好了懶得改,到最後還是用ArrayList修修補補,而在化簡的時候卻又用了一箇中間的HashMap,這種行為很多餘,很浪費。
另一點是自定義函式和Sum函式的處理,我是用的字串替換,但這實在是無計之策,一點也不優美,而且一旦函式定義可以遞迴或者巢狀,或者sum函式可以包含sum函式,我就寄了。
其實對於總體的架構我還是不太滿意,就是在資料的組織方面,但是自己又想不出來更好的。為什麼一定要分為Expr和Term兩個層次呢?這兩個層次明明是同構的,可不可以用一個東西把這兩個統一起來又讓它便於化簡呢?
其實這裡我考慮的是可拓展性,如果我們要加入更高層次的東西(我沒想出好的例子,希望你能理解我的意思)比如向量,向量的每一項如果都是一個Expr,那按照現在的架構,這一層的東西就得完全重寫了。
有沒有可能把Expr和Term這兩個東西抽象成同一個父類呢?似乎也不是很難,不過很多東西都要重新設計了。
說點好的:我覺得第一單元我的進步挺大。
感謝一路上幫助過我的朋友們,以及,第二單元加油鴨!