OO第一單元總結


OO第一單元總結

第一次作業

思路

  1. 將輸入標准化,即去掉空白字符、將++替換為+等。
  2. 按照正則表達式提取出每一項。
  3. 以每一項的指數為key,系數為value,建立一個HashMap<BigInteger, BigInteger>。而每找到一個新的項a*x**b時,就在hashmap中尋找是否存在一個項與之指數相同,若找到則合並,若未找到則直接把這個新的項插入hashmap。
  4. 之后求導、輸出即可。輸出若系數為1或-1或0、指數為1或0,則可化簡。

UML

LineCount

Metrics

metircs

  這還是我第一次接觸這樣的代碼復雜度的分析工具,所以特地去百度了一下。這些ev(G)就是我的代碼的圈復雜度、基本圈復雜度、環路復雜度,大概就是這些數值越高,說明代碼的流程圖越復雜、岔路什么的就越多,覆蓋測試就更困難,當然也就更容易出bug。這都這么高了,說明邏輯結構上還是有很大的改進空間——事實上只需要對項實現toString()方法,輸出系數指數就行,我寫的輸出函數,明顯是太過復雜了。

Bugs

  這第一次作業我就在互測環節被狠狠hack了,我們房間被hack數的三分之二都在我身上(悲)。我的錯誤在於,我在把項加入hashmap的時候偷懶,本想說一個項系數為0的時候就不要加入hashmap了,但是當兩項合並后結果的系數若為0,則這個合並的結果不會加入hashmap,原來的項也沒有從hashmap里出來,於是出了大問題。而這么大的問題居然在自己測試的時候沒有發現,到底是為什么呢?需要反思。

第二次作業

思路

  1. 依照參考書的形式化表述,逐層構建正則表達式(大正則),最終只需檢查輸入的字符串是否符合表達式的正則形式即可,不必過於糾結局部。
  2. 確定格式正確后進行化簡,基本思路同第一次作業。與第一次不同的是,我這次額外把+x-x+sin(x)等都換成+1*x**1-1*x**1+1*sin(x)**1的形式,確保每一項都是k * x**a * sin(x)**b * cos(x)**c的沒有任何化簡的形式,方便后續統一處理。至於優化什么的......沒有金剛鑽別攬瓷器活吧(心情復雜)。
  3. 建立HashMap<Key, BigInteger>,其中Key是每個項中x、sin(x)、cos(x)系數的三元組<a, b, c>,value則是這個項的系數k。每有一個新的項,若其能與HashMap中的項合並則合並,否則直接添加到HashMap中。
  4. 遍歷各個項,求出導數並輸出即可。

UML

UML2

LineCount

Linecnt2

Metrics

metrics2

  冷靜分析一下這次代碼的復雜度——

  • compareTo()是比較兩個Key是否相等,因為一個Key包含着三個元素,所以使用if語句的邏輯就比較復雜,復雜度似乎不能再降低。
  • getTerm()是在輸出答案字符串時,由HashMap中的鍵值生成字符串並化簡(如把1*x變成x),由於可能存在多種化簡的情況,就勢必有很多if語句,似乎也不能降低復雜度。
  • parsePoly()是我本次作業中寫的最迷糊、最混亂的一部分,甚至用了奇技淫巧才使得整個方法的行數小於60。大致就是先判斷字符串合不合法,在逐個取出項並把這些項放到HashMap中。這很顯然就是先后的三個動作嘛,完全可以拆分到三個函數中去,從而降低復雜度。所幸這部分和第一次作業整體結構差不多,才沒出大bug。

Bugs

  這次作業其實大體上和第一次作業差不多,所以萬幸,在強測和互測中沒有出現bug。

第三次作業

思路

  1. 一看到這次作業,我整個人是崩潰的,因子套因子這誰頂得住,何況我之前的代碼完全沒有考慮到函數嵌套的可能性,這下子絕對沒辦法向后兼容了,迫於無奈我只能——重構。
  2. 首先對於判斷輸入的合法性,由於嵌套結構的存在,單純使用正則表達式已經不可行,只能先判斷外層的合法性,再判斷內層的合法性。而多項式Poly和因子Factor的判斷方法不盡相同,同時Factor中可能包含Poly,Poly中也可能包含Factor,只能對這兩個分別寫兩個函數再互相調用。比如對於輸入cos((2*x**2 +- 3 *-x)) + x**2,其中((2*x**2 +- 3 *-x))是一個因子F,則把外層式子替換為cos(F) + x**2, 判斷外層是否符合一個多項式的格式要求,再對內層判斷其是否符合因子的格式要求。內層(2*x**2 +- 3 *-x),去掉括號后為2*x**2 +- 3 *-x,再判斷其是否為Poly。如此層層遞歸才能得到結果。當然也有大佬用的是表達式樹的方法,但是我不知道這樣的方法如何處理好幾個連續的運算符如--+2 * -x +- sin(x)(難道是把它們算作一個運算符?),所以沒有采用。
  3. 確定輸入合法后,將其進行化簡,方法同第二次作業。
  4. 遍歷整個表達式,將各項提取出來,項和項之間用加減法原則求導。項內部可能是因子相乘或單個因子,應用對應的求導法則即可。注意求導也是遞歸的過程,所以我采用的是將三角函數和冪函數統一設置為多項式Poly 的子類並實現返回值為Poly的求導方法。
  5. 至於化簡,我不配,我真的不配.......
  6. 實際上這個思路相當混亂,還請大佬輕噴。

UML

UML3

這結構實在是,太過混亂。

LineCount

LineCount3

Metrics

Metrics3

紅一大片如旭日東升。復雜度如此之高,只能說明自己的思路和代碼結構過於混亂。

Bugs

  這次的Bug倒不是出自代碼問題(驚了這么丑的代碼居然沒出問題),而是出自思路問題。在最初的思路中,由於完全放棄了任何形式的優化,我就想,只要在表達式中__遇到第一個能把表達式分成前后兩項的運算符號(即加減號),那么就可以把表達式分成前后兩部分,只需對前后兩部分應用求導法則就可以__。例如對2*x**2*sin((-x)) + cos((x**-1)) - x,找到的第一個加號就能把表達式分成2*x**2*sin((-x))cos((x**-1)) - x兩項,再對兩項分別求導之后加起來即可。前面的2*x**2*sin((-x))已經是個最小的項,就應用乘法法則和/或嵌套法則,后面的cos((x**-1)) - x還是若干個項相加減的形式,仍需遞歸地尋找各個項。

  但是這樣我完全忽略了一個問題——對於減號如x - 2*x + 3*x,用減號分成了x2*x + 3*x,前半部分導數為1,后半部分導數為5,結果得到1 - 5 = -4!這是因為我用減號分開后,把后半部分當成了一個整體,卻忘記了后半部分應該變號!鑒於變號是在太復雜,於是干脆把多項式中的每個項找出來,分別求導后再加減到一起。

  這個問題我認為是出在了偷懶上,自以為是地找到第一個加減號就直接把多項式分成兩半了。同時這個問題更是出在測試數據嚴重不全面的事實上。數據越是復雜,越是考驗我構造測試數據的能力。而這個能力,說實話我還遠遠不具備。

測試的策略

  無論是檢驗自己程序的正確性,還是在互測中找到別人代碼中的bug,都需要一套完善的測試策略。

  • 我首先想到的是自造評測機。利用Python中的exrex第三方庫,可以根據正則表達式隨機生成一個符合正則表達式的測試數據,再利用Python的sympy第三方庫實現對測試數據求導之后。之后只需向Python求出的導數和java求出的導數代入數值並比較結果是否相等即可。可以使用腳本或者Python程序實現此過程的自動化運行。
  • 第一次互測中,我實在不想看別人的代碼,於是直接讓評測機生成隨機數據去測試別人的代碼,hack中了就把這個數據交上去,屬實敷衍。由於提交自己代碼的時候評測機還沒搭好,所以沒有對自己的進行自動化評測,也就出了個bug。
  • 第二次互測前,我先用自動化評測把自己的測了挺久,覺得應該問題不大。互測中,我沿用上一次的策略,直接把別人的代碼甩給評測機,發現測不出bug。於是手動造了幾組自認為覆蓋比較全面的數據發現還是測不出bug,於是放棄。
  • 第三次的測試數據由於存在着復雜嵌套,單純用正則表達式是難以生成數據的(但是現在一想好像其實生成數據也不是那么難),加上時間不足、心態爆炸,我甚至沒來得及搭建好一台評測機,在此立正挨打。然而互測時,我把自己測試時候的兩個括號層層嵌套的極端數據交上去居然還hack成功了......

  關於同質bug我想說的:我第一次和第三次作業都被瘋狂hack,結果發現都是同質bug。雖然摁住別人的一個點死命刀真的很爽,但是確實沒啥必要,所以以后這樣的事我也是少干些為好,省得挨罵(小聲)。

對象創建模式

  第一次作業在一定程度上存在着向后兼容的可能,然而想法還是過於簡單,以至於只求完成當前任務。下周可能拓展什么功能之類的,完全沒有考慮,這是個嚴重的問題。

  第三次作業中見到了參考書的提示,思考之后覺得建立表達式樹還是有點困難,於是采用了自己的方法,於是萬劫不復。鑒於常數的求導很簡單於是沒有單獨建立類,現在看來這樣的處理不規范。此外建立了PowerFunc、SineFunc、CosineFunc類,都繼承自Poly類,在Poly類中實現getDeri()toString()方法,並在三個子類中重寫。這里我的Poly類中還實現了其他操作,所以這樣的處理也是不規范。

  實際上,從第二次作業開始,就可以使用工廠模式建立類——冪函數、正弦函數、余弦函數和常數,讓他們都繼承自一個函數抽象類,並實現求導接口。

體會和反思

  • 構造數據的能力過於弱,要么是完全交給評測機,要么只是簡單嘗試手動構造數據,雖然能構造出一些極限數據,但是根本沒有一套思維方法來保證自己測試數據的全面性。
  • 對於構造模式,特別是在第三次作業,明明指導書給了提示、自己也事先列了框架,但是偏偏在代碼實現中就把這些現成的、規范的模式拋到腦后,應該還是使用不熟練的緣故,回去好好看看工廠模式啊、多態啊、接口之類的知識才行。明明最近學的就是這些,思維方式千萬不能不掌握啊。
  • 關於心態:沒什么可說的,第三次作業看到題目的時候我人直接傻了,再看到討論區里大佬們討論的風生水起而且討論內容我還不太看得懂,直接就頂不住了啊。心態混亂之后的就是思路混亂,思路混亂之后的就是代碼混亂,代碼混亂又加劇心態混亂......雖說coding確實有樂趣但是也禁不住這么糟蹋啊。
  • 對於構思:以后的代碼肯定一次比一次復雜,必須提前構思,甚至可以提前到發題之前。
  • 對於面向對象:感覺自己似乎仍然被困在C語言的思維框架里,必須必須走出來才行,多看看代碼和網課吧!還應該有意識地多實用對象創建模式等。

最后圖一樂吧

一鄉二里共三夫子,不識四書五經六藝,竟敢教七八九子,十分大膽

十天九夜寫八java,測試七次六錯五改,強測WA四三二點,一只菜雞

鶯鶯燕燕翠翠紅紅處處融融恰恰

日日夜夜天天OO個個神神叨叨

圖畫里,龍不吟虎不嘯,小小書僮可笑可笑

網站上,神不言佬不語,區區我等抄誰抄誰


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM