第一次做業須要完成的任務爲簡單多項式導函數的求解。html
由於僅僅是簡單多項式的求導,因此求導自己沒有什麼可說的,直接套用冪函數的求導公式就好了,主要的精力是花在了正則表達式上。這裏推薦兩個網站:git
https://github.com/ziishaned/learn-regexgithub
https://regex101.com正則表達式
前者能夠用來學習正則表達式的語法,後者則提供實時的正則表達式匹配,方便進行調試和驗證。尤爲是後者,能大幅提升編寫正則表達式的效率和正確性,省去了每次都得在IDEA裏運行一遍的麻煩。編程
用正則時有一點須要注意,那就是大正則是不可取的。我一開始就是企圖用一大串正則匹配整個輸入,結果當時是爆棧了。合理的作法應當是每次只find()一個Term,以及經過Term與Term之間的鏈接關係判斷合法性。數據結構
在數據結構方面,我一開始用的是ArrayList,但在後來優化的時候發現合併不是常麻煩,因而果斷放棄,學習並採用了HashMap。經過將指數做爲Key,能夠快速地找到指數相同的項,以實現合併同類項的目的。多線程
因爲剛接觸Java語言,也不太懂什麼是面向對象思想,因此程序總體來講仍是比較C type的。也多是因爲本次做業比較簡單,因此也並無用到繼承、接口之類的東西。從類圖中能夠看到,一共也就兩個類,其中PolyDiff完成主函數、預處理、合法性判斷等任務,而Term則實例化出多項式中的每一項,保存係數和指數,並提供diff()求導方法。架構
能夠看出個別方法複雜度太高,這很不OO。下次應當多注意抽象以及代碼重用,避免一個方法或者類的過於臃腫。函數
公測未被查出bug。在互測階段,我第一個發現的bug是本身的bug,其緣由在於,我判斷合法性的作法是將匹配了的每一項用"#"替換掉,最後看除了" "、" \t"、"#"以外是否沒有其餘的字符。然而,我忽略了輸入一開始就含有"#"的可能,但這種可能性很是小,以致於我認爲只要不仔細閱讀個人代碼,基本上是不可能歪打正着的。遺憾的是,最終仍是有一位十分認真的同窗,(我猜)在一行一行通讀了個人整個代碼後,竟然還真的發現了這個bug……我是服氣的,真的太強了orz學習
至於我查別人的bug,都是用的我寫做業時自測發現了bug的樣例,由於我相信這樣的樣例會具備必定的殺傷力和普適性。事實證實,個人確能用這種方法測出不少別人的bug,因而也就沒有再結合他人的代碼設計結構來設計更多的測試樣例。
第二次做業須要完成的任務爲包含簡單冪函數和簡單正餘弦函數的導函數的求解。
第二次做業新引入了正餘弦函數,乍看複雜了許多,但其實仍是能夠用套公式的辦法死作,然而代價就是毫無擴展性可言。
對於每一項及其導函數,均可以化爲冪函數和正餘弦函數的冪次的乘積這種標準形式,那麼Term就相應擁有了係數、冪函數及正餘弦函數的指數這四個屬性。除了正則表達式匹配和求導規則有一些變化,其他與第一次做業並沒有太大區別。
值得注意的是,此次HashMap的Key我採用的是將冪函數及正餘弦函數的指數拼接成一個字符串,並在相鄰指數之間用"|"分隔。如此一來,即可省去重寫equals()和hashcode()的麻煩,而後像第一次做業同樣合併同類項。
程序結構大致相似於第一次做業。
合法性判斷的方法仍是有些冗長了,多是由於其中包含了一些對輸入進行預處理的操做,能夠考慮將這部分單獨抽離出來。
在吸收了上一次做業的教訓後,我捨棄了替換捕獲組方法,而是根據本次匹配起點是否爲上一次匹配的終點,以及最後一次匹配的終點是否爲字符串的末尾,來判斷輸入的合法性。最終在公測和互測中都未發現bug。
發現別人程序的bug依然採用上一次做業的方法,果真又查出了很多bug,大可能是對於非法的輸入沒有輸出WRONG FORMAT!
第三次做業須要完成的任務爲包含簡單冪函數和簡單正餘弦函數的導函數的求解(支持因子嵌套在三角函數因子中)。
正如以前所說的,套公式的作法是沒法解決任意層嵌套的問題的,所以整個程序架構必須重構。在這裏我採用的是遞歸降低的方法,規定好每種函數、組合的求導法則,將嵌套的內容看作一個總體,留給下一層遞歸處理,每次只判斷當前層的合法性並進行相應的求導。
在優化方面,僅僅是剔除了冗餘的0、一、括號、正負號等等,在同類項合併和恆等變換方面未能進行更多的處理,其緣由和求導方法的返回值有關。因爲我在一開始設計的時候就直接把每一個因子的求導結果看成字符串保存,因此整個表達式求導的過程就至關因而字符串按必定規則轉化和拼接的過程。這樣作的好處在於簡答、直觀、不易出錯,但弊端也很明顯,那就是很難作進一步優化,由於對字符串中的內容是沒法進行插入、刪除、替換、排序或合併等操做的,除非像讀取輸入那樣再一次對字符串進行分析。因爲時間有限,我沒有那樣作。其實更好的方法是將求導的結果也做爲某一個類的實例,方便對其作進一步的處理。
如圖所示,本次做業我用到了面向對象中的繼承,有必要這樣作的緣由有三:
至於Term,它並非因子,只是Expr的組成部分,因此單獨成爲一類。
經過繼承這種面向對象的思惟方式,咱們程序的結構更加清晰,功能更增強大,最重要的是,擁有了可擴展性。
果真越複雜的程序,要作到均衡控制每一個方法的複雜度就越難。主要的問題依然出在輸入分析、預處理以及合法性判斷上,儘管此次我將他們分離了開來。至於有沒有必要以及如何進一步拆解,還有待研究。
本次做業依然沒有被查出任何bug。
在查別人的bug方面,爲了解放勞動力,此次我選擇寫腳本進行批量測試。我用到了Python中一個強大的第三方包——SymPy,能夠進行復雜表達式的求導運算以及判斷兩式是否等價,這也是個人"簡化版評測機"的主要原理,即將Java程序的輸出與SymPy的輸出進行比對。顯然,該評測機沒法對非法輸入給出評判。而測試用例自己依然是我自測時手動構造的測試集,按行讀取即可進行批量測試,威力極大,效率奇高。
第一單元整體仍是比較入門的,重點在於熟悉Java語法特性、創建面向對象思惟,爲往後的多線程編程打好基礎。
對於我我的而已,學習了Java正則表達式、BigInteger類、HashMap、重寫與重載、繼承與接口等等技術,而且慢慢開始理解了層次化架構和麪向對象技術的優越性。整體感受Java仍是比C/C++要方便的多,基本上只要是你能想到的功能,總有類或者第三方包能爲你提供解決方案。而這就須要咱們有查找資料自學的能力,不管是看技術博客仍是閱讀官方文檔,再加上本身多動手實踐,相信很快就能掌握一項新的技術,並在做業中發揮其功能。
此外,經過Checkstyle對代碼風格進行規範也是比較有效的措施,基本上不會再寫出會使人抓狂的代碼了。可是其中每行最多80個字符的限制我實在以爲不太合理,尤爲是涉及正則表達式的時候,每每是硬生生地把一句邏輯連貫的代碼拆成多行。我的認爲在閱讀代碼時,莫名其妙的換行比一行稍長的代碼體驗更糟。建議將每行的字符數上限改成100~120。
對於面向對象的理解可能仍是隻是停留在表層,甚至會有一些誤解,這就須要進一步的學習和練習,在一次次實踐中總結經驗與教訓。但願之後能寫出更加OO的程序。