1. 程式人生 > 其它 >2022_BUAA_OO第一單元總結

2022_BUAA_OO第一單元總結

BUAA OO第一單元總結

寫在前面

首先祝賀我自己還算比較平穩地完成了OO的第一單元任務,儘管在強測中丟失了一些分數,但是在完成三次作業後,第一單元的設計過程還是給我帶來了很大的啟發。在本次部落格中,我將從作業架構、Bug分析、課程體會、單元總結四個方面介紹我OO第一單元的旅程。

一、作業架構

從我個人的理解出發,OO作業有一大特點就是"架構依賴"——只有建立起好的架構設計,我們才能在每一個層次舒服地使用資料結構進行資料處理。我與許多位同學討論過架構設計的基本思路,能夠在基準路線上達成一致;至於具體的實現方法,則不盡相同。我認為我的架構並不十分完善,更說不上優秀,但還是有一些優點,還是值得記錄一下,以便日後思考、總結、避雷。

三次作業的UML類圖及分析

此處首先宣告,由於本人的程式碼實現中不存在繼承關係、也不存在介面實現,但是的確具有一定的層次劃分,故而UML類圖中可能會根據需要新增一些箭頭以表示層次間的關係,請不要誤讀。另外,一些用於測試或是沒有使用的方法和變數就不在類圖中列出了。

第一次作業

架構及實現方法簡析

 

第一次作業的基本思路是利用正則表示式進行自上而下的解析,從而得到結果進行計算。由於不包含巢狀括號,只需一次解析即可計算結果。依照下面的層級進行解析計算即可。

$$
表示式\longrightarrow項\longrightarrow因子
$$

 

為此,設計了Expr、Term、Factor這三個類。首先重寫了Expr的構造方法,在構造Expr時需要給入一個String作為表示式輸入;Expr中的cutExpression

方法將會使用正則匹配的方法切分這一表達式,並將得到的子串用於構造一個Term,存入Expr自帶的一個ArrayList<Term>容器當中(這是參考了第一次實驗的寫法)。通過不斷的遍歷,可以將表示式徹底切分為一個一個的Term,並在Expr層次中儲存,便於在此層次呼叫下一層次的Term。這便是解析的第一步。自然而然地,我們便會聯想到解析的第二步該怎麼做——在Term類和Factor類中再次建立一層這樣的關係。這樣一來,我們就建立了一個三層的層次關係,並且能夠自上而下地將表示式解析為因子。

解析完畢,我們需要的是計算,與剛剛解析過程的自上而下相反,這是一個自下而上的過程。只要解析的思路足夠清晰,就會發現我們只需要在Term層次乘法計算其Factor並返回結果、在Expr層次加法計算Term並返回結果就好了。不過在此處我遇到了一個難題——處理的目標是字串,其中有亂七八糟的x、2*x、x**3這樣的因子,它們要如何做加法?如何做乘法?經過與同學的討論,我在解析的過程中添了一筆——將所有的因子都歸範圍a*x**b的標準形式

。這樣的好處不言而喻,只需在計算的時候提出係數(Ratio)和冪(Power)的部分即可進行普遍的計算。至此,一套清晰的解析思路就完成了;也正是這一次的分析,為我準備好了後面兩次作業的指導思想。

除了主要的解析思路之外,為了易於對字串進行處理,我構造了一個Formal類用於存放常用的處理字串的方法,並將它們定義為靜態方法(如除空白符、合併冗餘符號、字串轉BigInteger等);另外,構造了一個Count類存放多項式乘多項式的方法,計算的過程會呼叫這個類的方法。如此一來,整體的架構和層次便比較清晰了。

架構優缺點與艱辛的實現過程

優點:從總體上評價這次架構,是主體通透的的。自上而下和自下而上的兩個過程都比較清晰;靜態方法的構造提高了程式碼的複用率;且這是在我不知道遞迴下降的處理方法時想出來的,這使得我在後面的作業中都可以沿用這一套方法。其它的一些思路也很值得以後參考。至少在架構設計上,避免了"重構"。

缺點:當然,畢竟是第一次作業,缺點總是有的,這裡主要對映在一些細節上。例如,對於因子中的表示式項,我並未打算繼續將其作為表示式繼續解析處理,而是看作一個因子,在當層就計算得到結果並向上返回。這樣做有許多缺點:

  • 不能解析含有巢狀括號的表示式(雖然題目沒有要求,但主要原因是我沒有想到)

  • 極大削弱了程式碼的可擴充套件性(如果要新增一些其他的因子怎麼辦?又要重寫一遍Factor層的計算?)

  • 提高了解析與計算的耦合度

這是一個非常致命的缺點。從架構上不僅不健康,還使得我書寫程式碼的時候思路非常混亂:既要解析,又要計算。這直接導致了我第二次作業的徹底重構。

其它的缺點中比較令我印象深刻的還有:

  1. 沒提前規範好項和表示式的符號——這裡缺一個"+",那裡又有一個"+"

  2. 乘法計算的割裂。我將乘法分為單項式相乘、單項式乘多項式、多項式相乘,這其實很笨——但歸根結底,這一缺陷延伸自上面提到的"致命的缺點"。我固執地將只解析了一遍的因子看作"一切的終結",導致我割裂了乘法計算。

  3. 加法計算寫在瞭解析層次當中。我在Expr內書寫了一個"表示式加法"的方法,其實這樣不好,這會提高計算和解析的耦合度。

  4. 複用程式碼考慮不周。例如,取出括號中的內容應是非常常用的方法,但我卻在很多類中都寫了一遍這個方法;或許寫作一個靜態方法(當然,更加優秀的結構可以寫作繼承方法)是更好的選擇。

種種......

不過,即便是擁有了正確的架構設計,想要將架構順利實現也是非常有難度的。在第一次作業實現的過程中,我遇到了很多問題,磕磕絆絆地才完成了這一次作業。這裡的難度主要源自:

  • 本人剛開始接觸Java,很多方法的使用實在是不熟練,不知道該如何利用資料結構解決問題

  • 邊界情況的疏忽。出現0次冪多項式怎麼辦?出現複雜的多項式怎麼辦?

  • 種種不熟練導致的設計不統一。如將複用率高的程式碼進行抽離是實現過程中想到的方法,導致程式碼混亂。

  • 解析和計算的耦合過高,導致思路混亂。這一點在上面也提到過了。

  • 正則表示式的構建過於麻煩,極其容易出錯。

基於上面的種種原因,僅僅在第一次作業,我就經歷了一次重構、一次為時一天的Debug階段,歸根結底其原因是架構的細節沒有設計好。於是乎在第二次作業中,我對其中的一些方面進行了一定的改進。

基於度量分析第一次作業的程式結構(除MainClass)

程式碼規模

Class 屬性個數 方法個數 程式碼規模/行
Count 0 4 66
Expr 2 9 100
Expression 0 2 30
Factor 3 9 59
Formal 0 11 207
Term 3 9 96

類的OO度量

Class OCavg OCMax WMC
homework.Count 2.75 4.0 11.0
homework.Expr 2.2222222222222223 8.0 20.0
homework.Factor 1.4444444444444444 3.0 13.0
homework.Formal 3.5454545454545454 11.0 39.0
homework.Term 1.8888888888888888 4.0 17.0
homework.Test 3.0 3.0 3.0
task.Expression 2.5 4.0 5.0
Average 2.369565217391304 4.75 13.625

方法複雜度分析

Method CogC ev(G) iv(G) v(G)
homework.Count.normalNum() 0.0 1.0 1.0 1.0
homework.Expr.getExpression() 0.0 1.0 1.0 1.0
homework.Expr.getPower(String) 0.0 1.0 1.0 1.0
homework.Expr.getRatio(String) 0.0 1.0 1.0 1.0
homework.Expr.getTerms() 0.0 1.0 1.0 1.0
homework.Factor.getElements() 0.0 1.0 1.0 1.0
homework.Factor.getFactor() 0.0 1.0 1.0 1.0
homework.Factor.getIsNormal() 0.0 1.0 1.0 1.0
homework.Factor.setElements(ArrayList) 0.0 1.0 1.0 1.0
homework.Factor.setFactor(String) 0.0 1.0 1.0 1.0
homework.Factor.setIsNormal(int) 0.0 1.0 1.0 1.0
homework.Formal.cleanNote(String) 0.0 1.0 1.0 1.0
homework.Formal.standerString(String) 0.0 1.0 1.0 1.0
homework.MainClass.main(String[]) 0.0 1.0 1.0 1.0
homework.Term.Term(String) 0.0 1.0 1.0 1.0
homework.Term.getFactors() 0.0 1.0 1.0 1.0
homework.Term.getTerm() 0.0 1.0 1.0 1.0
homework.Term.setTerm(String) 0.0 1.0 1.0 1.0
task.Expression.cleanNote(String) 0.0 1.0 1.0 1.0
homework.Expr.Expr(String) 1.0 1.0 2.0 2.0
homework.Expr.cntExpr() 1.0 1.0 2.0 2.0
homework.Expr.cutExpression(String) 1.0 1.0 2.0 2.0
homework.Expr.printTerm() 1.0 1.0 2.0 2.0
homework.Factor.printElements() 1.0 1.0 2.0 2.0
homework.Formal.pickContent(String) 1.0 1.0 2.0 2.0
homework.Formal.pickFactors(String) 1.0 1.0 2.0 2.0
homework.Term.buildArray(String) 1.0 1.0 2.0 2.0
homework.Term.cutTerm(String) 1.0 1.0 2.0 2.0
homework.Term.printFactor() 1.0 1.0 2.0 2.0
homework.Count.mulSingle(String, String) 2.0 1.0 3.0 3.0
homework.Factor.Factor(String) 2.0 1.0 2.0 2.0
homework.Formal.switchPower(String) 2.0 3.0 1.0 3.0
homework.Term.cntTerm() 2.0 2.0 3.0 3.0
homework.Test.testMulti() 2.0 1.0 3.0 3.0
homework.Count.mulMulti(ArrayList, ArrayList) 3.0 1.0 3.0 3.0
homework.Factor.cutFactors() 3.0 1.0 3.0 3.0
homework.Formal.formalizeFac(String) 3.0 3.0 3.0 3.0
homework.Formal.onePow(String) 3.0 1.0 3.0 3.0
homework.Formal.switchInt(String) 3.0 3.0 1.0 3.0
homework.Formal.cleanString(String) 4.0 1.0 3.0 4.0
task.Expression.cleanString(String) 4.0 1.0 3.0 4.0
homework.Count.mergeFactor(String) 5.0 1.0 3.0 4.0
homework.Term.combine() 5.0 1.0 4.0 4.0
homework.Formal.formalPower(String) 10.0 1.0 6.0 6.0
homework.Expr.mergeTerm() 11.0 2.0 8.0 8.0
homework.Formal.switchExpr(String) 28.0 2.0 9.0 11.0
Total 102.0 55.0 100.0 109.0
Average 2.217391304347826 1.1956521739130435 2.1739130434782608 2.369565217391304

總體的工程量並不算巨大,但由於複用程式碼的規劃不佳,導致我在第一次作業中書寫了許多冗餘的方法,故而第三個表格十分冗長。類與類之間的界限算是比較明確,但是很多細節還是有待提高,這會體現在第二次作業中。

第二次作業

架構方法及實現簡析

 


第二次作業相比第一次作業,增加了表示式的擴充套件內容。這其中最主要的改動包括:

  • 新增自定義函式因子,不可遞迴定義、不可遞迴呼叫。

  • 新增求和函式因子,只能單獨使用。

  • 新增sin、cos三角函式。

當然,具體到細節層面還包括一些資料限制、IO修改等,不過這些改動只要是做過作業的同學都心知肚明。我認為我們最需要關注的就是因子種類的複雜化和括號層次的加深。經歷了第一次作業的毒打後,我決定先深度設計架構——包括各種細節——然後再進行具體實現。總體的解析思想沒有發生特別大的變化,但我開始進行遞迴解析的嘗試。

$$
表示式\longrightarrow項\longrightarrow因子\longrightarrow整數因子、冪函式因子、三角函式因子、表示式因子、自定義函式因子、求和函式因子
$$

 

上面是一次解析的完整過程。相比於第一次作業,我在因子解析的時候增加了型別判斷的系列方法,並且對每一種因子專門書寫了一個類用於其標準化。為了便於計算,我將因子分出兩個基本類:

$$
三角函式因子:[+-][(sin)|(cos)][(expression)]**n
$$

 

$$
普通因子:[+-]a*x**b
$$

 

這兩類因子是計算的根源——當然,第二次作業的三角函式因子自帶的表示式內部還不需要計算——進行多輪解析的目標就是為了得到這兩類因子。於是乎,除了這兩類因子外的所有因子,我都將其看作表示式對待,送入一個新的Expr進行解析。進行了足夠輪次的解析後,總能夠得到最基本的兩類因子。然後我們可以通過自下而上的計算,得到最終的結果。

在Expr、Term和Factor類仍舊是寫入了一些變數和解析、計算的方法。另外,在第一次作業的基礎上抽離出了一些複用率高的方法,寫入Formal類成為靜態方法;強化了Count類,用於存放加法和乘法的方法;新建了一個CustomFunction類,用於存放自定義函式,便於各層次使用。

優化

在第二次作業當中,對第一次作業的一些不足之處進行了優化:

  • 符號歸一化。每次一解析出的內容,都會經過一個addNote方法來新增串首符號,才能夠被繼續解析處理。

  • 拋棄正則的切分識別方法,改用模擬棧的迴圈遍歷進行項和因子的切分。這是一個非常大的改進,例如對於表示式:

$$
-a+(b-c)
$$

 

進行將表示式切分為項的模擬。從第一個字元開始遍歷,並在迴圈之初設定一個int型別的變數pairBracket,用於記載括號層數,其初始值為0。遇到左括號加上1,遇到右括號減去1;第一次遇到加減號,則進入一個內層迴圈,開始摘取切分個體的內容。摘取過程中,再次遇到加減號且括號層數為0,則結束摘取。即對上面表示式摘取的結果就是得到了兩個項:

$$
-a、+(b-c)
$$

 

很顯然,這種方法也適用於"將項切分為因子"的過程。當然,遇到一些特殊情況需要特殊的處理,在此不贅述。

  • 進一步降低計算和解析的耦合度。本次,我直接將所有的計算方法搬出瞭解析類,於是寫程式碼的時候感到十分的清晰:該寫什麼就寫什麼,不會出現第一次作業的混亂現象。

  • 程式碼複用優化。進一步歸納了複用率高的方法,並寫入靜態方法類中。

  • 除去了不需要的類與方法

這些優化使得我在實現基本功能時更加順利。但是本次設計在設計和細節上也有許多考慮不周之處,使得我在強測中丟失很不少分數,這會在下面做介紹。

缺點

本次設計的缺點也是很明顯的。

  • 採用ArrayList容器放置基本因子,使得加減乘計算、合併同類項以及化簡的操作十分困難。這是因為當時我不會使用HaspMap存放資料,我在這裡吃了很大的虧。

  • 沒有考慮一些新增因子對計算帶來的麻煩。這使得我的計算方法也非常醜陋且麻煩,無法將三角函式合併而只能直接追加到已有表示式的後方,丟失了很多效能分。這裡的失誤和第一個缺點也有很大的聯絡。

  • 對細節的考慮不周(0次冪的符號、自定義函式的符號都沒有考慮周全)

  • 將多項式因子仍看作一個"因子",導致因子層次還需要考慮加法。(這一點在第三次作業中進行了力所能及的優化的嘗試)

  • 基本項還不夠基本。將三角函式和冪函式區分開來看待,其實並不優越,反而會帶來很多麻煩,因為這會使得我們缺少統一的處理方法。

總的來講,這一次的設計其實是有一些不全面的——對解析的過程做了很好的規劃設計,但是對計算、化簡的考慮十分不周全,導致到最後只能提交一個破爛的版本;寫好的優化版本也是Bug層出不窮。由於對細節考慮不周,在強測中也失掉了三個點的分數,非常不值得。在第三次作業中,我在這些方面進行了一些優化嘗試,不過結果還是不盡人意,程式設計能力還是有待加強啊

基於度量分析第二次作業的程式結構(除MainClass)

程式碼規模

Class 屬性個數 方法個數 程式碼規模/行
Count 0 7 110
CustomFunction 1 3 26
Expr 4 6 80
Factor 2 8 244
Formal 0 17 256
Term 4 7 92

類的OO度量

homework.Count 2.5714285714285716 8.0 18.0
homework.CustomFunction 1.3333333333333333 2.0 4.0
homework.Expr 2.3333333333333335 8.0 14.0
homework.Factor 4.375 8.0 35.0
homework.Formal 3.6470588235294117 18.0 62.0
homework.MainClass 2.0 2.0 2.0
homework.Term 2.4285714285714284 10.0 17.0
Average 3.1020408163265305 8.0 21.714285714285715

方法複雜度分析

homework.Count.getPower(String) 0.0 1.0 1.0 1.0
homework.Count.getRatio(String) 0.0 1.0 1.0 1.0
homework.Count.normalNum() 0.0 1.0 1.0 1.0
homework.Count.plusTerm(String, String) 0.0 1.0 1.0 1.0
homework.CustomFunction.addFunctions(String) 0.0 1.0 1.0 1.0
homework.CustomFunction.getFunctions() 0.0 1.0 1.0 1.0
homework.Expr.Expr(String, CustomFunction) 0.0 1.0 1.0 1.0
homework.Expr.getFinalExpression() 0.0 1.0 1.0 1.0
homework.Expr.setFinalExpression(String) 0.0 1.0 1.0 1.0
homework.Expr.setInitExpression(String) 0.0 1.0 1.0 1.0
homework.Factor.getFinalFactor() 0.0 1.0 1.0 1.0
homework.Formal.hideSin(String) 0.0 1.0 1.0 1.0
homework.Formal.mergeNote(String) 0.0 1.0 1.0 1.0
homework.Formal.mergePower(String) 0.0 1.0 1.0 1.0
homework.Formal.mergeReturnMinus(String) 0.0 1.0 1.0 1.0
homework.Formal.mergeReturnPower(String) 0.0 1.0 1.0 1.0
homework.Formal.shortenExpr(String) 0.0 1.0 1.0 1.0
homework.Formal.showSin(String) 0.0 1.0 1.0 1.0
homework.Term.Term(String, CustomFunction) 0.0 1.0 1.0 1.0
homework.Term.getFinalTerm() 0.0 1.0 1.0 1.0
homework.Term.getInitTerm() 0.0 1.0 1.0 1.0
homework.Term.setFinalTerm(String) 0.0 1.0 1.0 1.0
homework.Term.setInitTerm(String) 0.0 1.0 1.0 1.0
homework.CustomFunction.printFunctions() 1.0 1.0 2.0 2.0
homework.Expr.cntTerm() 1.0 1.0 2.0 2.0
homework.Formal.addNote(String) 1.0 2.0 1.0 2.0
homework.MainClass.main(String[]) 1.0 1.0 2.0 2.0
homework.Term.cntTerm() 1.0 1.0 2.0 2.0
homework.Count.typeJudge(String) 2.0 2.0 1.0 2.0
homework.Factor.Factor(String, CustomFunction) 2.0 1.0 2.0 2.0
homework.Formal.cleanWhiteNote(String) 3.0 1.0 3.0 3.0
homework.Formal.fetchContent(String) 3.0 1.0 3.0 3.0
homework.Formal.fetchSum(String) 3.0 1.0 3.0 3.0
homework.Formal.normalizeFactor(String) 3.0 3.0 3.0 3.0
homework.Factor.analysisComplex(String) 4.0 5.0 5.0 5.0
homework.Factor.analysisNormal(String) 5.0 3.0 3.0 3.0
homework.Formal.fetchAfterEqual(String) 5.0 1.0 3.0 4.0
homework.Factor.analysisSum(String) 6.0 2.0 3.0 5.0
homework.Count.multiFactor(String, String) 7.0 1.0 4.0 4.0
homework.Formal.getVariable(String) 7.0 1.0 4.0 4.0
homework.Factor.analysisExpr(String) 9.0 3.0 5.0 5.0
homework.Factor.analysisFunc(String) 11.0 4.0 6.0 8.0
homework.Factor.analysisTri(String) 12.0 2.0 6.0 6.0
homework.Formal.reverseSymbol(char, String) 15.0 2.0 5.0 7.0
homework.Count.getSingle(String) 19.0 8.0 8.0 8.0
homework.Expr.cutExpression() 19.0 8.0 8.0 8.0
homework.Formal.splitExpr(String) 19.0 8.0 8.0 8.0
homework.Term.cutTerm() 28.0 8.0 9.0 10.0
homework.Formal.mergeFactor(String) 46.0 6.0 17.0 18.0
Total 233.0 100.0 141.0 152.0
Average 4.755102040816326 2.0408163265306123 2.877551020408163 3.1020408163265305

由於許多地方採用了迴圈遍歷的分析手段,導致部分類和方法的複雜度有所提高。Formal類和Factor類承擔了最重要的解析任務,故而其複雜度最高。但是我對程式的耦合程度和部分類的複雜度還是比較滿意的——實現解析和計算兩個功能部分的程式碼區分的比較開,且Expr類和Term類的複雜度都不高。

第三次作業

本次作業新增的要求可用一句話概括:允許各種括號巢狀。基於第二次作業的思路,已經可以對這種要求進行處理了;但是為了彌補優化的缺陷,我還是進行了一次重構,想要通過增加新的層次進行更進一步的優化。提前預告一下,因為種種原因,這次嘗試也失敗了,但是我還是想記錄一下,畢竟這是一條可能的道路。

經過第二次作業的考驗,我認為我對解析和計算兩個部分的關係已經有了比較好的瞭解,就沒有再使寫額外的靜態方法進行計算操作,而是全部寫在了類層次當中。

 

解析的基本思路沒有發生變化,但是為了更好地進行儲存計算,在層次裡新增了一個Basic類,用於應對Factor是多項式的情況;另外,將基本的因子進行了重新規範,我給它起名叫做"基本項":

$$
[+-]a*x**b*[((sin)|(cos))(expression)**n]*
$$

 

這樣的好處不言而喻——我們在計算的時候終於不用左右兼顧,直接對上面的格式寫一個計算的方法就好。為了這樣的優化,Basic類要有這些功能:

  • 儲存a*x**b的係數和冪

  • 使用容器HashMap儲存三角函式的三角部分和指數部分

  • 將儲存的資料組合成字串,向上返回

且由於HaspMap的特性,在解析的時候就可以進行簡單的同類項合併(遍歷key集合即可),我認為這樣的儲存方法和結構是很優越的。

優化

本次作業相對第二次作業的優化其實並不多,因為在主要的資料結構和字串處理上第二次作業已經做得比較好了。比較大的進步是在新增一個Basic層次之後,複雜因子及三角函式的計算變得簡單了起來。其餘還有一些小的修改:

  • 改進了自定義函式的引數識別方法。之前是使用split()方法直接劈分形如f(x,sin(x),x**3)這樣的式子得到引數。但當函式能夠遞迴呼叫之後,這樣的方法在遇到f(x,f(x,x))時便會出錯。對此,我採用了之前提到過的"模擬棧+迴圈遍歷"的辦法解決了問題。(棧真是個好東西!)

  • 將計算的方法融合到了層次結構中,減少了靜態方法的宣告。

  • 優化了帶冪函式的表示式因子的解析方法。原本是直接對其進行展開計算,現在改為展開後繼續解析並計算。

失敗的效能優化嘗試

在修改了儲存結構後,新的結構理論上應該更便於效能優化。然而我由於忙於其他事情,始終沒能抽出時間完成這一部分,在強測中丟失了效能分。這算是一個遺憾吧

基於度量分析第三次作業的程式結構(除MainClass)

程式碼規模

Class 屬性個數 方法個數 程式碼規模/行
Basic 4 16 193
CustomFunction 1 3 26
Expr 4 11 158
Factor 4 21 317
Formal 0 9 75
Term 4 11 126
Operate 0 4 41

類的OO度量

task.Basic 2.8125 11.0 45.0
task.CustomFunction 1.3333333333333333 2.0 4.0
task.Expr 3.090909090909091 8.0 34.0
task.Factor 2.9047619047619047 8.0 61.0
task.Formal 2.0 7.0 18.0
task.MainClass 2.0 2.0 2.0
task.Operate 1.5 2.0 6.0
task.Term 2.3636363636363638 10.0 26.0
Average 2.5789473684210527 6.25 24.5

方法複雜度分析

task.Basic.getBasicStr() 0.0 1.0 1.0 1.0
task.Basic.getPower() 0.0 1.0 1.0 1.0
task.Basic.getRatio() 0.0 1.0 1.0 1.0
task.Basic.getTriFunc() 0.0 1.0 1.0 1.0
task.Basic.initializeBasic() 0.0 1.0 1.0 1.0
task.Basic.separateTri() 0.0 1.0 1.0 1.0
task.Basic.setBasicStr(String) 0.0 1.0 1.0 1.0
task.Basic.setPower(BigInteger) 0.0 1.0 1.0 1.0
task.Basic.setRatio(BigInteger) 0.0 1.0 1.0 1.0
task.CustomFunction.addFunctions(String) 0.0 1.0 1.0 1.0
task.CustomFunction.getFunctions() 0.0 1.0 1.0 1.0
task.Expr.Expr(String, CustomFunction) 0.0 1.0 1.0 1.0
task.Expr.getFinalExpression() 0.0 1.0 1.0 1.0
task.Expr.setFinalExpression(String) 0.0 1.0 1.0 1.0
task.Expr.setInitExpression(String) 0.0 1.0 1.0 1.0
task.Factor.Factor(String, CustomFunction) 0.0 1.0 1.0 1.0
task.Factor.addBasic(Basic) 0.0 1.0 1.0 1.0
task.Factor.analysisFunc(String) 0.0 1.0 1.0 1.0
task.Factor.getBasics() 0.0 1.0 1.0 1.0
task.Factor.getFinalFactor() 0.0 1.0 1.0 1.0
task.Factor.getInitFactor() 0.0 1.0 1.0 1.0
task.Factor.opedBracket(String) 0.0 1.0 1.0 1.0
task.Factor.setFinalFactor(String) 0.0 1.0 1.0 1.0
task.Factor.setInitFactor(String) 0.0 1.0 1.0 1.0
task.Formal.hideSin(String) 0.0 1.0 1.0 1.0
task.Formal.mergeNote(String) 0.0 1.0 1.0 1.0
task.Formal.mergePower(String) 0.0 1.0 1.0 1.0
task.Formal.mergeReturnMinus(String) 0.0 1.0 1.0 1.0
task.Formal.mergeReturnPower(String) 0.0 1.0 1.0 1.0
task.Formal.showSin(String) 0.0 1.0 1.0 1.0
task.Operate.getPower(String) 0.0 1.0 1.0 1.0
task.Operate.getRatio(String) 0.0 1.0 1.0 1.0
task.Term.Term(String, CustomFunction) 0.0 1.0 1.0 1.0
task.Term.addFactor(Factor) 0.0 1.0 1.0 1.0
task.Term.getFactors() 0.0 1.0 1.0 1.0
task.Term.getFinalTerm() 0.0 1.0 1.0 1.0
task.Term.getInitTerm() 0.0 1.0 1.0 1.0
task.Term.setFinalTerm(String) 0.0 1.0 1.0 1.0
task.Term.setInitTerm(String) 0.0 1.0 1.0 1.0
task.Basic.backString() 1.0 1.0 2.0 2.0
task.CustomFunction.printFunctions() 1.0 1.0 2.0 2.0
task.Expr.printFinalTermAndFinalFactor() 1.0 1.0 2.0 2.0
task.Expr.printTerm() 1.0 1.0 2.0 2.0
task.Expr.printTermAndFinalFactor() 1.0 1.0 2.0 2.0
task.Expr.spliceTerms() 1.0 1.0 2.0 2.0
task.Formal.addNote(String) 1.0 2.0 1.0 2.0
task.MainClass.main(String[]) 1.0 1.0 2.0 2.0
task.Operate.fetchContent(String) 1.0 1.0 2.0 2.0
task.Operate.noteJudge(BigInteger) 1.0 2.0 1.0 2.0
task.Term.printFactor() 1.0 1.0 2.0 2.0
task.Term.printFinalFactor() 1.0 1.0 2.0 2.0
task.Basic.Basic(String) 2.0 1.0 2.0 2.0
task.Basic.mulBasic(HashMap, HashMap) 2.0 1.0 3.0 3.0
task.Factor.analysisExpr(String) 2.0 1.0 2.0 2.0
task.Factor.analysisFactor(String) 2.0 1.0 2.0 2.0
task.Factor.analysisTri(String) 2.0 1.0 2.0 2.0
task.Factor.fetchFunction(String) 3.0 1.0 3.0 3.0
task.Factor.getFunction(String) 3.0 1.0 4.0 4.0
task.Formal.cleanWhiteNote(String) 3.0 1.0 3.0 3.0
task.Basic.addTriFunc(String, BigInteger) 4.0 3.0 4.0 4.0
task.Basic.backMergedString() 4.0 1.0 3.0 3.0
task.Factor.analysisComp(String) 4.0 1.0 5.0 5.0
task.Factor.analysisSum(String) 4.0 1.0 3.0 3.0
task.Factor.expandPower(String) 4.0 2.0 4.0 4.0
task.Factor.analysisNor(String) 5.0 1.0 3.0 3.0
task.Term.countFactors() 7.0 1.0 5.0 5.0
task.Expr.countTerms() 9.0 4.0 6.0 6.0
task.Factor.findPos(String, String) 9.0 3.0 7.0 8.0
task.Factor.splitVar(String) 15.0 7.0 6.0 8.0
task.Formal.reverseSymbol(char, String) 15.0 2.0 5.0 7.0
task.Expr.cutSplicedTerms(String) 17.0 8.0 6.0 8.0
task.Factor.cutBasics(String) 17.0 8.0 6.0 8.0
task.Expr.cutExpression() 19.0 8.0 8.0 8.0
task.Basic.splitBasicStr() 20.0 10.0 10.0 11.0
task.Basic.normalMerge() 24.0 11.0 10.0 11.0
task.Term.cutTerm() 28.0 8.0 9.0 10.0
Total 236.0 140.0 182.0 196.0
Average 3.1052631578947367 1.8421052631578947 2.3947368421052633 2.5789473684210527

真是太多表格了!這隻能怪我重構了三次(笑)。不過肉眼可見,第三次作業的複雜度和結構相對於第二次作業也有了提升。這說明我對結構的掌握也有了一定的提升畢竟重構了三次

小結

完成第一單元的任務後,我感覺還是收穫良多。雖然沒有體會到迭代開發的樂趣,但是在經過了多次重構以及層次架構設計

二、Bug分析

公測與互測

首先列出我在三次公測與互測中的測試情況

作業次數 弱中測 強測 互測
Homework1 通過 通過 0/21
Homework2 通過 三個點WA 1/11
Homework3 通過 通過 4/12

第一次作業的完成情況最佳、第三次之、第二次再次。但得分並不能準確反映我對題目和程式設計思想的理解,我認為我在第三次作業中的收穫是最大的。不過,分數並不重要,分析總結自己的錯誤,吸取經驗教訓才是最重要的,我將舉出並分析我在測試中程式犯下的經典錯誤。

Homework1

在第一次作業中,儘管沒有被發現Bug,但是作業截止前的Debug階段還是讓我難以消受。

零次冪:忘記對零次冪表示式因子的特殊處理,發生了執行時錯誤。

正則表示式出錯:正則表示式無法判斷一些輸入情況,屬於考慮上的疏忽。但不得不承認正則表示式的切分方法很容易出錯。這也導致我在第二次作業中更換了切分的方法

Homework2

第二次作業的問題主要也是由於考慮不周。

自定義函式符號出錯(強測):自定義函式解析完畢向上返回的時候,忘記按照規範在串首判斷是否需要新增符號,導致有時正負解析錯誤。

零次冪(強測、互測):表示式因子出現零次冪時,直接返回1,忘記根據其符號判斷是否需要返回-1

Homework3:

第三次作業的問題在互測被發現了。

變號出錯(互測):一般計算-(a+b)這樣的式子時,要先計算內部,再反轉符號;而我的程式先反轉了內部的符號,再進行計算。這是一個比較大錯誤,我也很驚訝這個錯誤沒在強測中被測出來。

求和函式sum(互測):本次作業中,求和函式可能是這種形式:

$$
sum(i,s,e,i**2)
$$

 

在正常的輸入輸出當中,形如2**3這種資料是非法的;但是根據求和函式的定義,上面這種情況卻是可能出現的!但我的程式並沒有對這種"合法的非法情況"進行考慮,故而發生了執行時錯誤。修復這個Bug,只需要在替換所有的i時套上括號即可。

小結:

總體上來講,我做的還是比我的預期好很多的。儘管很多分數都不應該丟掉,但是對於程式設計能力較弱的我來說,還是應該一步一步來。本次的Bug有很多都邊界情況發生了問題;也有一些邏輯上的問題。很多師兄建議我學習一下自動化測試的方法,我現在也開始深感其必要性,畢竟程式設計師要對自己的程式碼負責

Hack

介紹完我的Bug,也要介紹一下我"幫別人Debug"的思路。其實我並不是一個熱衷於Hack別人的人,但是這個階段還是比較有意思的;活躍度也關乎成績。我Hack別人的主要思路大致如下:

  • 邊界。我犯的錯誤,別人也可能反。在給自己的程式Debug過程中,會使用零次冪、0+0、超長整數等邊界情況進行測試

  • 針對優化進行的攻擊。有的同學會進行一些追求極致效能的優化,這會使得他們在一些簡單情境下犯錯。

  • 針對解析方法進行的攻擊。尤其是正則解析法,這種解析方法很容易出錯。

  • 共享資料。一些同學提供的測試點也很有參考價值

由於不會自動化測試,我的Hack並不算猛烈。但不能因為要Hack而去學習測試,學習測試永遠是為了自我提高

三、總結分析

1.設計體會

由於最初的架構不佳,幾乎每一次作業我都進行了一次重構。不過,雖然程式碼在不斷重構,設計思想卻是不斷迭代的。從最初的只能解析一層的、計算和解析糅合在一起的程式,到最後的能夠進行多層解析、耦合度低的程式,這期間設計的思想是經過了很大的完善和修補的;為了順利編寫程式碼,我用於設計而寫的手稿也有幾張草稿紙之多,儘自己所能規範了每一處的細節處理方法。唯一可惜的就是重構佔去了我很多的時間,導致我沒能夠進行很多的優化,這算是第一單元的一些遺憾了。不過還是在第一單元的設計過程中收穫良多,"領會精神"永遠是更重要的。

2.本階段OO課程的心得體會

首先一個字:難!

到目前為止的OO作業,包括pre、第一單元、上機訓練,都給我一種很強的壓迫感。作業截止時間的緊迫、程式量的巨大、對Java語言的陌生、設計思路的新穎,對我來說都是很大的阻力。聽吳際老師說,以後會將pre課程放入暑期學期單獨開課,我私以為這是很好的做法,畢竟不是人人都有很好的程式設計基礎,或者說是幾天速成的自學能力,或者說是願意在寒假預習。有Java基礎的同學,對各種資料結構的使用應當是信手拈來。當然,想要解決難題,也不僅僅要靠基礎,還需要學會自己搜尋資源、積極與同學討論交流設計思想,這都是解決困難的很好的辦法。我希望未來的OO課程在不失去難度的同時,能夠讓更多人更好地上手。

另外,助教團隊的高度負責精神也讓我很是感動。精心設計的題目和指導書、面對突發情況的高效解決、對同學疑問的耐心解答等,都是每位同學有目共睹的。北航六系許多優秀的課程也都是在強大的助教團隊下打磨而成。我感到很幸運,能在如此優秀的助教團隊的陪伴下學習這門課程。

最後,第一單元的旅程總算是結束了,我希望我能記得我這四周的所學所感,並在之後的OO課程以及學習生涯中再接再厲。