面向對象項目一總結感想

一.自我代碼分析java

1.度量:express

第一次做業:編程

 

 

第二三次做業(改動很小,給出第三次做業結果):數據結構

 

 

2.整體自我評價:eclipse

  第一次做業代碼實現糟糕,能夠從代碼統計結果看到這一點,第二三次感受比較滿意,思路順暢方法簡單可擴展性強但面向對象思想還不夠(甚至沒有繼承和接口),倒算是比較精緻的面向過程,不過類的高內聚低耦合卻是實現得不錯函數

3.詳細分析
性能

  總之,我三次小做業的代碼是逐漸迭代優化的,不論是思路上仍是結構上。測試

  第一次做業較爲簡單,但是個人思路相對於後兩次倒是最複雜的,因爲當時沒有確立面向對象思想,整個java項目就是個面向過程的僞c程序——在一個Item類裏定義了許多靜態方法,而後在main裏調用(沒錯,只有這兩個類),甚至連求導都是在輸出時直接拼湊字符串一項一項輸出,優化也是直接在字符串上操做。優化

  第一次做業較簡單,我實現的也極爲糟糕,因此接下來重點談後兩次做業,因爲後兩次做業個人思路徹底一致,幾乎沒有改動,因此這裏就一塊兒說了。spa

  第二次做業佈置下來後,雖然尚未要求括號嵌套(或者說尚未要求表達式因子的處理)。但是我已經預感到了未來必定是要處理涉及遞歸定義的表達式的(下意識地就想到了編譯課設的表達式)。因此我先寫了一個小小的詞法分析程序來切出運算符、變量等,多虧了上學期的編譯,因此這下寫起來是輕車熟路。詞法分析共定義瞭如下幾個類:

 

  全部函數的命名和功能都和編譯課設的保持一致(再複習一下),Symenum,枚舉類型,定義了標識符;Judge(裏面全是靜態方法,高內聚),我把全部詞法分析用到的boolean返回類型都放到了這裏;cut,切出帶符號整數、變量的字符串等等(當時未雨綢繆,假設變量不僅有x)。須要注意的是個人程序把讀入的表達式做爲全局變量(main函數裏的public靜態變量),把當前分析到的字符串下標也設置爲了public靜態變量,因此能夠看到個人這些函數有許多未傳入參數。至於checkindex這個參數原本是想進行預讀的,不事後來也沒用到預讀。

  熟悉編譯的同窗都知道,詞法分析以後就該語法語義分析了,至於錯誤處理?在這個過程當中if else 順手就處理了。可能仍是面向過程思想有點重吧,我雖然想到了要定義個表達式類、項類、因子類、運算符類等,可仍是沒能實現,而是忍不住順着編譯的思路再寫了個表達式分析:

 

  單看返回值就很明瞭了,我用了。就是把運算符作中間節點、變量或常量作葉子的樹(一元運算符只有左孩子),這樣設計一來是較爲直觀,構造時anafactor等這些函數只須要返回個節點而後讓調用者掛在已有的樹上(這是個遞歸的過程),二來樹結構遞歸求導很方便。上源碼:

 

  這裏只給出了anafactor的代碼,第二次做業不容許表達式因子,就在分析出表達式因子不是x時報錯,第三次做業調用anaexpression函數就是。

這樣,分析完輸入字符串,我就獲得了一顆表達式樹,怎麼管理樹呢,天然是再來個類;

 

  這是個你們夥……直逼上限五百行(489),我程序的核心功能幾乎都在這個樹上(求導、優化、輸出)。先說求導:

表達式樹上求導是個十分簡單的過程,是個遞歸的過程,我設計的是求導獲得一棵新樹,對應不一樣運算符生成不一樣的樹就好了:

 

  而後是優化,個人優化分爲兩部分,一個是剪枝,一個是合併同類項。

剪枝:

 

 

 

  剪枝主要就是剪掉或化簡一些01啊參加運算的的樹枝,這是頗有必要的,由於一旦機械地按照求導規則求導,會產生許多乘0啊乘1啊,將他們優化掉會大大提升性能,我第二次做業在這上面吃了大虧,求完導不剪枝,直接重構(一會說重構,這是第二次做業的特殊狀況),運算量很大,強測直接有四個點超時,差點掛了。

  剛纔提到了重構,這裏解釋一下,重構也就是展開括號,由於第二次做業括號裏只容許有x,而個人表達式樹直接輸出的話必定要給優先級低的運算符加括號的,因此須要在樹上摘摘掛掛變化把低優先級的運算符提到高優先級運算符上層,這樣遞歸輸出沒必要加括號結果也是正確的。附上代碼,有興趣的同窗能夠看看(就是小學的乘法分配律):

 

  第二階段的優化就是合併同類項了,個人思路是每一個節點有個綜合屬性,有個繼承屬性,繼承屬性從根向下傳遞,遇到加減號中止傳遞,遇到乘號繼續向兩顆子樹下傳遞;綜合屬性從葉子向上傳,遇到加減號繼續向上傳,遇到乘號乘入乘號的繼承屬性並中止下傳。用形象點的語言說,就是每一個運算符先獲得它左孩子的表達式,再獲得它右孩子的表達式(這裏的表達式不是string,是數據結構),而後根據這個運算符的類別進行兩個表達式的合併,將合併後的表達式向上傳(由於是遞歸程序,因此能夠自下向上傳遞)。代碼:

 

二.bug分析

  我這次測試出的bug分爲兩種:其一爲對規則理解不透徹準確的bug,其二爲本身手抖的「筆誤型」bug

  第一類bug印象較深的有兩處,一次是第二次做業的弱測有一個輸入是「什麼都不輸入」,注意這還不是輸入空串,我是用try catch解決的。第二次就是對輸出的字符串格式要求和輸入一致,我一開始沒仔細理解這句話,致使輸出的格式不對,因此後來我會把每次的輸出字符串再扔進程序跑一遍,二次迭代(二階導)也正確就說明程序出bug的可能性更小。

  第二類bug就是我把一處「cos」寫成了「sin」,爲此還跟蹤了挺長時間,之後要更仔細些。

  值得慶幸的是並無由於程序錯誤或混亂而出現的bug,我以爲這種bug可能更難解決吧,因此編程前必定要有清晰的構思,確保本身的方案是正確的。

整體來講,bug在輸入輸出處理及邏輯較複雜、代碼較長的方法裏出現的頻率更高些,個人解決方法通常是寫完一個小功能(如詞法分析)就測試一下,防止bug積累,往後面對長長的幾千行代碼,bug就更難找。

  另外關於測試的一點體會,其實測試樣例也不必定要越複雜越好,尤爲是涉及到遞歸程序時,其實只要一層對了、兩層對了,bug出現的機率也不大了,更深層的測試也不能說明更多,反而一味追求輸入複雜度可能漏掉其餘的一些分支,所以要理智地分析不一樣類型、不一樣分支的輸入,爭取實現全覆蓋(至於代碼覆蓋,eclipse有自帶的插件,仍是很方便的)。

三.嘗試重構

  仍然採用樹結構,可是樹節點及頁節點定義一個父類,對不一樣的運算符、數字、變量定義不一樣的子類,重載求導、優化等方法,減小本身的判斷語句及分支。

相關文章
相關標籤/搜索