第一單元總結 作業總結 一 程式結構分析 因為在此之前並沒有接觸過 java 語言或者是與之相似的語言, 而不用說 OO 的概念, 所以一切都是全新的. 開始的時候腦子並沒有任何面向對象的概念, 類這個概念僅僅建立在 C 語言的某個 .c 文件之上, 簡單地認為 java 中的類就是 C 中的 ...
第一單元總結
作業總結
一 程式結構分析
因為在此之前並沒有接觸過 java 語言或者是與之相似的語言, 而不用說 OO 的概念, 所以一切都是全新的. 開始的時候腦子並沒有任何面向對象的概念, 類這個概念僅僅建立在 C 語言的某個 .c 文件之上, 簡單地認為 java 中的類就是 C 中的 .c 文件. 所以第一次作業幾乎沒有什麼類劃分, 僅有的四個類更像是 C 中的四個函數, 而不是四個類. 第二次作業的時候毫無疑問地走向了重構道路, 雖然仍然沒有擺脫面向過程的編程方法, 但開始對類的概念有一些自己的想法, 開始嘗試著劃分類, 並用類之間的交互來實現需要的功能. 第三次作業中, 類的數量和種類大大增多, 同時類的劃分界限也明確起來, 並不是一味地當成函數集合來用. 很明顯, 這三次並不那麼令人愉快的作業經歷大大地加深了我對面向對象思想的理解. 下麵是三次作業的詳細分析:
三次作業類圖
三次作業度量
名詞說明:
OCavg:
Calculates the average cyclomatic complexity of the non-abstract methods in each class. Inherited methods are not counted for purposes of this metric.
類平均圈複雜度, 繼承類不計入
WMC:
Calculates the total cyclomatic complexity of the methods in each class.
類總圈複雜度
ev(G):
Calculates the essential complexity of each non-abstract method. Essential complexity is a graph-theoretic measure of just how ill-structured a method's control flow is. Essential complexity ranges from 1 to v(G), the cyclomatic complexity of the method.
非抽象方法的基本複雜度, 基本複雜度是一個圖論度量理論, 用來度量一個方法的控制流結構有多差, 基本複雜度的範圍是 1 到 v(G)
iv(G):
Calculates the design complexity of a method. The design complexity is related to how interlinked a methods control flow is with calls to other methods. Design complexity ranges from 1 to v(G), the cyclomatic complexity of the method. Design complexity also represents the minimal number of tests necessary to exercise the integration of the method with the methods it calls.
設計複雜度, 度量方法控制流與其他方法之間的耦合程度, 設計複雜度的範圍是 1 到 v(G)
v(G):
Calculates the cyclomatic complexity of each non-abstract method. Cyclomatic complexity is a measure of the number of distinct execution paths through each method. This can also be considered as the minimal number of tests necessary to completely exercise a method's control flow. In practice, this is 1 + the number of if's, while's, for's, do's, switch cases, catches, conditional expressions, &&'s and ||'s in the method.
圈複雜度, 度量每個中不同路徑執行的數量
度量結果:
第一次:
Class metrics:
class OCavg WMC com.lm.Merge 6.0 6.0 com.lm.Main 6.0 6.0 com.lm.Item 2.857142857142857 20.0 com.lm.IfLegal 4.0 8.0 Total 54.0 Method metrics:
method ev(G) iv(G) v(G) com.lm.PolyGetter.pureOperators() 1.0 11.0 11.0 com.lm.PolyGetter.PolyGetter(String) 1.0 1.0 1.0 com.lm.PolyGetter.getItems() 1.0 3.0 3.0 com.lm.PolyGetter.delSpace() 3.0 3.0 4.0 com.lm.Merge.merge(Item[]) 5.0 5.0 6.0 com.lm.Main.main(String[]) 2.0 6.0 6.0 com.lm.Item.setExp(BigInteger) 1.0 1.0 1.0 com.lm.Item.setCoff(BigInteger) 1.0 1.0 1.0 com.lm.Item.Item(String) 1.0 6.0 7.0 com.lm.Item.getItem() 3.0 6.0 7.0 com.lm.Item.getExp() 1.0 1.0 1.0 com.lm.Item.getCoff() 1.0 1.0 1.0 com.lm.Item.derive() 1.0 3.0 3.0 com.lm.IfLegal.judge() 6.0 4.0 8.0 com.lm.IfLegal.IfLegal(String) 1.0 1.0 1.0 Total 29.0 53.0 61.0 Average 1.9333333333333333 3.533333333333333 4.066666666666666 第二次:
Class metrics:
class OCavg WMC com.mypkg.PolyGetter 6.0 6.0 com.mypkg.Main 5.0 15.0 com.mypkg.Item 2.4166666666666665 29.0 Total 50.0 Average 3.125 16.666666666666668 Method metrics:
method ev(G) iv(G) v(G) com.mypkg.PolyGetter.parse(String) 6.0 4.0 7.0 com.mypkg.Main.merge(ArrayList) 4.0 7.0 7.0 com.mypkg.Main.main1() 2.0 8.0 8.0 com.mypkg.Main.main(String[]) 1.0 2.0 2.0 com.mypkg.Item.setSinExp(BigInteger) 1.0 1.0 1.0 com.mypkg.Item.setPowExp(BigInteger) 1.0 1.0 1.0 com.mypkg.Item.setCosExp(BigInteger) 1.0 1.0 1.0 com.mypkg.Item.setCoff(BigInteger) 1.0 1.0 1.0 com.mypkg.Item.itemDerive() 1.0 1.0 1.0 com.mypkg.Item.Item(String) 3.0 11.0 12.0 com.mypkg.Item.Item(BigInteger,BigInteger,BigInteger,BigInteger) 1.0 1.0 1.0 com.mypkg.Item.getSinExp() 1.0 1.0 1.0 com.mypkg.Item.getPowExp() 1.0 1.0 1.0 com.mypkg.Item.getItem() 1.0 9.0 9.0 com.mypkg.Item.getCosExp() 1.0 1.0 1.0 com.mypkg.Item.getCoff() 1.0 1.0 1.0 Total 27.0 51.0 55.0 Average 1.6875 3.1875 3.4375 第三次:
Class metrics:
class OCavg WMC com.mypkg.Sin 6.333333333333333 19.0 com.mypkg.Production 3.875 31.0 com.mypkg.Pow 3.3333333333333335 10.0 com.mypkg.Main 4.2 21.0 com.mypkg.Int 1.0 3.0 com.mypkg.Cos 6.333333333333333 19.0 com.mypkg.Conbination 22.0 66.0 Total 169.0 Average 6.035714285714286 24.142857142857142 Method metrics:
method ev(G) iv(G) v(G) com.mypkg.Sin.Sin(String) 1.0 12.0 12.0 com.mypkg.Sin.print() 1.0 6.0 6.0 com.mypkg.Sin.derive() 1.0 8.0 8.0 com.mypkg.Production.Production() 1.0 1.0 1.0 com.mypkg.Production.print() 1.0 14.0 14.0 com.mypkg.Production.derive() 1.0 11.0 11.0 com.mypkg.Production.add(Sin) 1.0 1.0 1.0 com.mypkg.Production.add(Pow) 1.0 1.0 1.0 com.mypkg.Production.add(Int) 1.0 1.0 1.0 com.mypkg.Production.add(Cos) 1.0 1.0 1.0 com.mypkg.Production.add(Conbination) 1.0 1.0 1.0 com.mypkg.Pow.print() 1.0 3.0 3.0 com.mypkg.Pow.Pow(String) 1.0 3.0 3.0 com.mypkg.Pow.derive() 1.0 4.0 4.0 com.mypkg.Main.pureString(String) 1.0 11.0 12.0 com.mypkg.Main.nextIndex(String,int) 4.0 4.0 6.0 com.mypkg.Main.main1(String[]) 4.0 4.0 4.0 com.mypkg.Main.main(String[]) 1.0 2.0 2.0 com.mypkg.Main.bracketMatch(String) 3.0 3.0 5.0 com.mypkg.Int.print() 1.0 1.0 1.0 com.mypkg.Int.Int(String) 1.0 1.0 1.0 com.mypkg.Int.derive() 1.0 1.0 1.0 com.mypkg.Cos.print() 1.0 6.0 6.0 com.mypkg.Cos.derive() 1.0 8.0 8.0 com.mypkg.Cos.Cos(String) 1.0 12.0 12.0 com.mypkg.Conbination.print() 1.0 9.0 10.0 com.mypkg.Conbination.derive() 1.0 9.0 10.0 com.mypkg.Conbination.Conbination(String) 22.0 51.0 66.0 Total 57.0 189.0 211.0 Average 2.0357142857142856 6.75 7.535714285714286
優缺點分析:
- 從上面的類圖和度量表看到, 我在這幾次作業中並沒有使用繼承和介面, 一方面是因為我覺得自己對於繼承和介面的概念不夠熟悉, 怕亂用寫出 BUG, 加大自己的工作量, 另一方面, 我對於介面的作用確實不理解, 也不會用(直到現在仍然是認為介面只是形式上規定了繼承於他的類應該有哪些方法, 而和我不用介面分別在各個類中去實現這些方法並沒有什麼實質區別, 僅僅是形式上而已).
- 從三次作業的度量值分析可以看到, 我幾次的代碼都比較病態, 尤其是到了第三次, 幾乎所有的代碼都集中到了 Conbination 類, 而且在 Conbination 類中的構造方法也是占據了極高的比例, 方法, 類之間的耦合度極高, 這些都暴露出來我編程思想不夠嚴謹的問題, 我希望在以後的學習中努力改變自己這種病態的代碼結構.
- 從三次類圖的遞進發展來看, 類漸趨於完善, 可以看到進步也是存在的. 這三次作業最大的收穫就是對面向對象思想的理解得到了極大程度地加深, 尤其是看到第三次作業提示構造表達式樹, 有一種豁然開朗地感覺. 其次, 則是正則表達式, 在 OO 課開課之前多次聽說正則表達式地大名, 但都是好像很叼地樣子, 覺得應該不是一個初學者地我能掌握地東西, 所以沒怎麼看. 但是經過三次作業地磨礪, 我覺得自己對正則表達式已經有了一定的瞭解, 開始覺得正則表達式很難, 接觸之後又覺得正則表達式就那麼回事, 挺簡單的, 然後大正則爆棧, 開始瞭解一些和正則相關的深入一些地知識, 如: DFA, NFA, 回溯等概念. 又開始覺得正則是個博大精深的東西. 相信在經歷了 OO 之後, 我一定能很好地運用正則.
二 程式BUG分析
第一次作業
第一次作業一共兩個已知 BUG , 分別是:
超大整數陷阱: 我在比較兩個超大整數的時候, 想當然地調用了 intValue() 方法, 將他們轉換 int 型數據比較, 未考慮超大整數溢出, 結果出錯了. 應該調用 Compare() 方法進行超大整數之間的比較.
大正則陷阱: 試圖用一個超長的正則表達式匹配整個表達式, 結果爆棧. 正確方法應該是分項匹配. 下麵貼出大正則反例片段:
Pattern q = Pattern.compile("^" +
"\\s*[+-]?\\s*(([+-]?\\d+)" +
"|(([+-]|[+-]?\\d+\\s*\\*)?\\s*x\\s*(\\^\\s*[+-]?\\d+)?))" +
"(\\s*[+-]\\s*((([+-]|[+-]?\\d+\\s*\\*)?\\s*" +
"x(\\s*\\^\\s*[+-]?\\d+)?)|[+-]?\\d+))*" +
"+" +
"\\s*$");
Matcher n = q.matcher(line);
if (n.find()) {
return true;
}
Pattern p = Pattern.compile("^" +
"\\s*[+-]?\\s*(([+-]?\\d+)" +
"|(([+-]|[+-]?\\d+\\s*\\*)?\\s*x\\s*(\\^\\s*[+-]?\\d+)?))" +
"(\\s*[+-]\\s*((([+-]|[+-]?\\d+\\s*\\*)?\\s*" +
"x(\\s*\\^\\s*[+-]?\\d+)?)|[+-]?\\d+))*" +
"\\s*$");
Matcher m = p.matcher(line);
return m.find();
第二次作業
第二次作業未發現 BUG
第三次作業
第三次作業發現一個潛在 BUG ( 未被測出來, 但確實存在 ), 就是老僧長談地深層遞歸爆棧, 目前並沒有想到什麼好的解決辦法. 另一個已知 BUG 是 sin(- 1) 會判對, 原因是我在處理原始字元串地時候採用了窮舉帶符號數被空格截斷地情況, 但是窮舉不夠充分, 僅考慮到了冪指數, 以及最外層的整數繫數和常數項, 未考慮到整數作為因數出現嵌套的情況, 故錯.
三 Hack體驗
第一次作業因為超大整數溢出直接沒進互測, 沒有任何體驗....
第二次作業沒有被炸, 體驗良好....
第三次被炸了幾個點....
總結幾次互測的體驗, 發現 BUG 高發區集中在輸入格式處理上面, 大正則爆棧, 情況考慮不夠完善等. 另外很多同學的 BUG 是由於優化導致的, 以至於將優化方法或者類註釋掉就通過了....另外有一點要說的是, 希望提交間隔可以適當縮短一點, 畢竟有的數據點自己驗證起來也不好驗證, 特別是經過一系列恆等變換之後.
四 建模重構
關於重構, 初步的想法是構建一個運算介面, 運算介面包括求導, toString(), 簡單化簡(僅僅是同類項合併, 指數合併等)方法. 然後構建 Sin Cos Pow Com Mul 類繼承運算介面, 構建表達式樹, 實現鏈式求導, 指導書上說構建嵌套類, 但我不知道怎麼構造, 只能通過在 Sin Cos Pow 內部使用嵌套標記來表示該對象是否嵌套了下一級, 希望在之後公佈的優秀代碼種學會如何構建嵌套類. 至於化簡, 隨緣吧...