1. 程式人生 > 其它 >OO Unit1 總結

OO Unit1 總結

Unit1

綜述

第一單元的任務主要是進行包含冪運算的表示式化簡,在隨後幾次作業的迭代開發後可以實現支援冪函式,括號巢狀,有限個自定義函式(及其巢狀),三角函式與求和函式。主體架構採用遞迴下降演算法拆分表示式為Expr(表示式)、Term(項)、Factor(因子),後對於拆分好的字串進行計算、化簡與合併同類項。

一、程式架構分析

HW1

  • 內容簡述

    實現一個包含加、減、乘與乘方的表示式展開與化簡,允許出現至多一層括號

  • 程式碼架構

    • 基本類及其行為

       

其中,可以分為遞迴下降部分、儲存因子及計算兩部分。

Lexer與Parser為遞迴下降法先拆分後按類讀取字串的過程,對錶達式進行初步的解析。通過遞迴下降演算法,將表示式分為Expr,Term與Variable,其內部邏輯與指導書中形式化表示貼合。

其中Expr表示:空白項 [加減 空白項] 項 空白項 | 表示式 加減 空白項 項 空白項

Term→ [加減 空白項] 因子 | 項 空白項 * 空白項 因子

Variable→ 變數因子 | 常數因子 | 表示式因子。

其中,最為關鍵的基本項為Variable類,用以表示最基本同時涵蓋所有型別因子的情況:

a*x**b

我為其設定了三個元素來充分的表示:Cofficient(常數),Power(冪次),Negitive(符號),在合併同類相前,式子應被化簡為n個variable相加減的形式。由此,我完成了作業一在架構方面的基本構思。

關於拆分後表示式的計算

初步的考慮採用遞迴的方法將因子計算,返回上一層項中計算,最後返回表示式類,再合併同類項輸出結果。最後因為時間(和心態QWQ)的問題,沒有在架構中成功實現,而採用了將每個項中的元素暴力相乘,最後一併在expr中進行加減,過長的method與多層巢狀的ifelse結構,為無窮的bug製造了絕佳的機會…也預示了後續迭代的痛苦重構:(

  • 複雜度分析

  • (非常驚人的資料,由於在Main中做了大量字串的預處理,當做一次教訓了

  • 化簡與合併同類項

    • 儲存的容器十分關鍵,用Hashmap儲存Variable,key為power,value為Cofficient,方便計算與合併

    • 一些細節的優化如:cofficient = 1、power = 0的情況,與x**2->x*x;

       

HW2

  • 內容簡述:在HW1的基礎上增加了三角函式、求和函式與自定義函式,多餘括號仍為最多一層。

    對於求和函式與自定義函式,我採用不失語義的帶入法(通俗而言就是帶括號的帶入加細節處理),對錶達式進行預處理,轉化為符合HW1形式化表述的的表示式後,套用第一次的計算與化簡。

  • 程式碼架構分析

     

在基本類中增加了三角函式類與自定義函式類,對於新增項各自放到類中處理完後,以表示式的形式新增到原有表示式中,同樣參與計算即可。此時,計算部分的巨大mothed還沒有重構(由於懶惰hhh),好在各種細節注意到了,沒有在互測環節傷痕累累。

  • 複雜度分析

Method CogC ev(G) iv(G) v(G)
Lexer.Lexer(String) 0 1 1 1
Lexer.getCos() 8 5 3 6
Lexer.getNumber() 2 1 3 3
Lexer.getPower() 3 1 7 7
Lexer.getSin() 8 5 3 6
Lexer.next() 13 2 8 9
Lexer.peek() 0 1 1 1
Parser.Parser(Lexer) 0 1 1 1
Parser.parseExpr() 5 1 4 4
Parser.parseFactor() 25 9 14 14
Parser.parseTerm() 2 1 3 3
advance.preCheck() 8 1 8 9
advance.setStr(String) 0 1 1 1
cal.cal1() 27 1 8 8
cal.setExpr(Expr) 0 1 1 1
expr.Cos.Cos(String) 0 1 1 1
expr.Expr.Expr() 0 1 1 1
expr.Expr.addTerm(Term) 0 1 1 1
expr.Expr.cal(Term) 91 1 19 19
expr.Expr.getMi() 0 1 1 1
expr.Expr.getTerm() 0 1 1 1
expr.Expr.getTerms() 0 1 1 1
expr.Expr.setMi(int) 0 1 1 1
expr.Expr.toString() 3 1 3 3
expr.Function.getExpr() 0 1 1 1
expr.Function.getItem() 0 1 1 1
expr.Function.getName() 0 1 1 1
expr.Function.setExpr(String) 0 1 1 1
expr.Function.setName(char) 0 1 1 1
expr.Function.setVariable(String) 4 3 2 3
expr.Number.Number(BigInteger) 0 1 1 1
expr.Number.toString() 0 1 1 1
expr.Sin.Sin(String) 0 1 1 1
expr.Term.Term() 0 1 1 1
expr.Term.addFactor(Factor) 0 1 1 1
expr.Term.addNegitive(boolean) 0 1 1 1
expr.Term.caculate() 11 2 6 6
expr.Term.getFactors() 0 1 1 1
expr.Term.getNegitive() 0 1 1 1
expr.Term.toString() 3 1 3 3
expr.Variable.Variable(BigInteger, int) 0 1 1 1
expr.Variable.addCofficient(BigInteger) 0 1 1 1
expr.Variable.addCos(String, int) 7 3 3 6
expr.Variable.addNegitive1(boolean) 0 1 1 1
expr.Variable.addSin(String, int) 7 3 3 6
expr.Variable.copyCos(HashMap<String, Integer>) 0 1 1 1
expr.Variable.copySin(HashMap<String, Integer>) 0 1 1 1
expr.Variable.getCoefficient() 0 1 1 1
expr.Variable.getCos() 0 1 1 1
expr.Variable.getNegitive1() 0 1 1 1
expr.Variable.getPower() 0 1 1 1
expr.Variable.getSin() 0 1 1 1
expr.Variable.toString() 24 2 11 12

複雜度重災區仍然是計算cal部分,沒有和整體架構融為一體的計算模組雖然完成了相關任務,但是卻非常不利於迭代開發。

  • 化簡與合併同列項

    由於表示式最終仍被化簡為

    Variable+Variable+

    的形式,故新新增的因子同樣應該以一定的形式儲存在Variable中

    對於Sin與Cos,採用Hashmap<String,Integer>以三角函式內expr為key,三角函式的power為value,方便合併同類項

HW3

  • 內容分析

    在作業二的基礎上增添多層括號巢狀與自定義函式巢狀

  • 程式碼架構

    與第二次的UML圖幾乎完全相同,計算的環節完全重構,採用字尾表示式的方式來解決括號巢狀的問題,而對於表示式巢狀,採用遞迴帶入的方式處理,總體由第二次作業迭代開發完成。

  • 複雜度分析

     

Method CogC ev(G) iv(G) v(G)
Func.Func(HashMap<Integer, Function>, String) 0 1 1 1
Func.checkFunc() 155 18 29 31
Lexer.Lexer(String) 0 1 1 1
Lexer.getCos() 8 5 3 6
Lexer.getNumber() 2 1 3 3
Lexer.getPower() 3 1 7 7
Lexer.getSin() 8 5 3 6
Lexer.next() 13 2 8 9
Lexer.peek() 0 1 1 1
Parser.Parser(Lexer) 0 1 1 1
Parser.parseExpr() 5 1 4 4
Parser.parseFactor() 27 9 15 15
Parser.parseTerm() 1 1 2 2
advance.preCheck() 8 1 8 9
advance.setStr(String) 0 1 1 1
caculate.cal() 23 4 16 16
caculate.getCos() 8 5 3 6
caculate.getNumber() 2 1 3 3
caculate.getSin() 8 5 3 6
caculate.muLit(Expr, Expr) 21 1 8 8
caculate.setExpr(String) 0 1 1 1
sinCos.simPlify() 18 7 6 9
sinCos.sinCos(String) 0 1 1 1
Method CogC ev(G) iv(G) v(G)
expr.Cos.Cos(String) 0 1 1 1
expr.Expr.Expr() 0 1 1 1
expr.Expr.addStandExpr(Variable) 0 1 1 1
expr.Expr.addTerm(Term) 0 1 1 1
expr.Expr.copyExpr(Expr) 0 1 1 1
expr.Expr.fan() 4 1 3 3
expr.Expr.getMi() 0 1 1 1
expr.Expr.getStandExpr() 0 1 1 1
expr.Expr.getTerm() 0 1 1 1
expr.Expr.getTerms() 0 1 1 1
expr.Expr.setMi(int) 0 1 1 1
expr.Expr.toString() 11 1 6 6
expr.Function.getExpr() 0 1 1 1
expr.Function.getItem() 0 1 1 1
expr.Function.getName() 0 1 1 1
expr.Function.setExpr(String) 0 1 1 1
expr.Function.setName(char) 0 1 1 1
expr.Function.setVariable(String) 4 3 2 3
expr.Number.Number(BigInteger) 0 1 1 1
expr.Number.toString() 0 1 1 1
expr.Sum.SumString() 7 1 5 5
expr.Sum.setBegin(BigInteger) 0 1 1 1
expr.Sum.setSum(String) 0 1 1 1
expr.Term.Term() 0 1 1 1
expr.Term.addFactor(Factor) 0 1 1 1
expr.Term.addNegitive(boolean) 0 1 1 1
expr.Term.caculate() 11 2 6 6
expr.Term.getFactors() 0 1 1 1
expr.Term.getNegitive() 0 1 1 1
expr.Term.toString() 3 1 3 3
expr.Variable.Variable(BigInteger, int) 0 1 1 1
expr.Variable.addCofficient(BigInteger) 0 1 1 1
expr.Variable.addCos(String, int) 7 3 3 6
expr.Variable.addNegitive1(boolean) 0 1 1 1
expr.Variable.addSin(String, int) 7 3 3 6
expr.Variable.copyCos(HashMap<String, Integer>) 0 1 1 1
expr.Variable.copySin(HashMap<String, Integer>) 0 1 1 1
expr.Variable.getCoefficient() 0 1 1 1
expr.Variable.getCos() 0 1 1 1
expr.Variable.getNegitive1() 0 1 1 1
expr.Variable.getPower() 0 1 1 1
expr.Variable.getSin() 0 1 1 1
expr.Variable.setMultiItem(boolean) 0 1 1 1
expr.Variable.toString() 35 2 14 15

二、bug分析

第一次作業:

  計算過程過於暴力以至於式子太長時出現多種情況的處理不好,一直在修改cal內部的邏輯。

第二次作業:

  強測錯了四個點,互測時被hack了一刀,一共查出了三個bug:

  • 符號的問題,在expr中toString中多設定了符號的判斷,導致錯誤。

  • 第二個是類似於x*-1的問題

  • 第三個是式子結果為0的情況,應判斷空串情況再輸出。

第三次作業:

  強測錯了四個點,互測時被hack了sum的範圍,用的long型別,找到了同房間小夥伴的一些bug,主要有以下:

  • 大數bug,互測的同學都在爆int爆long(互相傷害),使用了int或long儲存資料,而非BigInteger造成了bug。

  • sum函式的bug:出現s1n,s2n的情況;上界小於下界的情況;BigInteger負數的情況;sum巢狀的情況…

  • 零次冪bug,這部分bug第一次作業應該就修過了,但是遇到sin和cos函式,自定義和sum函式引數替換,以及層層巢狀關係後,有時零次冪又會輸出奇怪的結果。

三、Hack策略分析

  我一般只是手動輸入一些資料,比如x**0、x*-2*-1、1**+0001等等一些簡單易錯的資料,還有一些邊界大數的資料,充分利用程式碼架構中最冗雜邏輯性較差的部分找出來漏洞去hack,用自己的bug樣例…在對自己的程式做了較多測試,積累了一定bug的經驗之後,對於相似架構的程式碼可以嘗試同樣的方法hac。

四、收穫總結

  在第一單元的三次作業中,我逐漸學習到了面向物件程式設計的思想和方法,深刻體會到到了一個良好的架構對程式迭代開發的重要性,設計是重中之重,在開發之初就應該保有著為迭代做準備的想法,同時代碼風格檢查也讓我改正了很多不好的習慣。以下是我在第一單元的主要收穫:

  1. 在著手開始寫之前,把基本的程式碼架構想好,不可以邊寫邊重構:( 把各個類、函式的關係搞清楚.

  2. 仔細閱讀指導書,深入理解形式化表述的深層邏輯,思考程式設計的目的,與迭代的可能方向,在指導書中有著出題者的意圖與做法的而推薦,都應該仔細理解消化。

  3. 重構要趁早,趕到第三次作業再去面對一個迭代效能幾乎為0的程式碼內心實際是崩潰的,如果沒有辦法再一開始就避免無法迭代需要重構的情況,就在發現問題後立馬著手去做,對於壞的架構瘋狂豐富細節的意義不是很大

五、心得體會

  因為寒假的Pre沒有認真做好,前三週經歷了一個邊學基礎知識邊處理問題的痛苦經歷,思路的匱乏和無限的重構,耗費了很多的時間,但是收穫也很多,可以說是一個被逼著快速入門的過程了QWQ   另外,我充分感覺到了面向物件這門課對個人程式碼能力的要求,作業發下來後對著題目坐牢一天,才能開始著手實現,有時需要和同學討論,求教助教等等,實現前的壓力和實現後的成就感反覆疊加…第二單元增加對於架構與迭代的考慮,避免走第一單元過度重構的彎路。

壓力雖大但此應必走之路,困難仍存但有迎難而上之心,第二單元繼續加油!