1. 程式人生 > 其它 >北航2022面向物件第一單元:表示式解析和化簡

北航2022面向物件第一單元:表示式解析和化簡

北航2022面向物件第一單元:表示式解析和化簡

1. 發現的典型問題

1.1 物件深拷貝

在使用物件時,應該尤其注意物件的屬性是否在各種操作下都保持不變。特別是那些管理其他物件的物件。如果兩個容器類儲存了相同的物件引用,其中一個修改時,會把另一個容器中的物件一起修改,從而導致不可知的後果。

就這三次做的作業而言,時間上的要求不高,因此可以把所有管理資料的類全部設定成不可變的。或者獲得一個新的物件,必須完全地拷貝原來物件的全部資訊,包括容器內部的物件必須是地址不同的。

1.2 字串提取和解析

比較好的字串解析方法是遞迴下降,這樣就不用在一個方法內處理所有的問題,一個方法只用處理一種運算即可,邏輯比較簡單和清楚。但是這樣就必須知道目前頂層是哪一種運算。

可以使用棧來判斷頂層的運算。比如要判斷該層是否是加法,可以遍歷字串,如果遇到左括號就壓棧,遇到右括號就彈棧,只有遇到加號,而且此時棧為空時,才表示這個加號在頂層。

同樣的方法可以用來分割函式的引數。函式引數是用逗號分割的,而且引數可以是其他函式的呼叫。我聽到一種做法處理函式引數,就是把每兩個逗號作為分隔符,每組分別判斷是否是分隔符。這樣時間開銷相當大,而且分開的字串形式複雜,不好判斷。可以使用棧的方法解析函式引數字串。如果遇到逗號,而且此時棧為空,就說明這個逗號一定是頂層的,可以作為分隔符。

1.3 加減號和正負號的判斷

在第一次作業時,很多同學遇到了問題,不知道怎麼區分加減號和正負號。在第一次作業時,我用到的是無腦判斷。如果遇到一個 '+' 號,而且此時棧為空,就直接遞迴解析符號左右的字串。如果左右都是合法的字串,就可以認為這是個加號。

這種做法應該是沒有邏輯問題的,根據遞迴分析,錯誤的字串會在底層被識別,把錯誤資訊逐層上傳回來。但是在第二次作業中,因為這種方法我被hack了兩次,然而不是報邏輯錯誤,而是超時。這是因為輸入的字串非常長,每個項都有一個加號和一個正號。這樣我處理每個項,都必須遞迴分析整個字串。這種方法的時間複雜度是指數函式,字串稍微長一點耗時就非常長了。因此我被迫做了特判,就是字串的結尾不能是 '+' ,'-','*' 號,否則直接返回字串非法。因為輸入的字串一定是合法的,每次遞迴下降都是有效操作,這樣就保證了每次操作的時間開銷都是必要的。

2. 分析自己的程式

2.1 數量度量

指標:

指標名 作用物件 含義 方向
LOC 類/方法 程式碼行數 越小越好
LCOM 類中內聚度的缺乏,值越小說明內聚度好,符合高內聚要求 越小越好
FANIN 扇入表示呼叫該模組的上級模組的個數,值越大表示覆用性好。 越大越好
CC 方法 圈複雜度,值越大說明程式程式碼質量低,且難以測試和維護 越小越好

第一次作業

類:

類名 簡介 LOC LCOM FANIN
ExpFactory 用來構造表示式 154 0.625 0
Expression 表示式 102 0 3
Term 表示式中的項 62 0.286 1
Main 主類 10 -1 0

方法(只記錄主要方法):

類名 方法名 簡介 LOC CC
ExpFactory createExp 建立表示式 45 11
createExpByTerm 建立項 46 12
createExpByFactor 建立因子 32 7
simplifyStr 簡化字串 3 1
Expression toSimpleStr 簡化字串 28 7
power 乘方 18 4
multiExp 乘表示式 18 4
subExp 減表示式 4 1
negate 取負號 5 2
addExp 加表示式 14 3
addTerm 新增一個項 9 2
Term toSimpleStr 簡化字串 40 13

第二、三次作業

因為第二、三次作業的設計思路基本一樣,所以用第三次作業來說明

類:

類名 簡介 LOC LCOM FANIN
Main 主類 47 -1 0
AddSubFunc 加減函式 23 0 3
Func 函式基類 117 0 10
FuncFactory 用來建構函式 227 0 2
FuncLib 自定義函式庫 105 0 2
MulFunc 乘法函式 18 -1 2
NumFunc 常元函式 19 0 4
OpFunc 正負函式 19 0 2
PowerFunc 指數函式 23 0 2
SumFunc 求和函式 64 0.5 2
TriFunc 三角函式 23 0 3
VarFunc 變元函式 23 0 6
Output 用來輸出的類 150 0 3
StrUtil 字串工具類 37 -1 3
Term 輸出類的組成單元 157 0 2

方法(只記錄主要方法):

類名 方法名 簡介 LOC CC
Func toOutput 得到輸出的物件 52 12
replaceFormal 將所有變元替換為新函式 12 4
replace 將該函式節點替換為新函式 10 2
duplicate 複製 23 4
FuncFactory createFunc 建立函式 47 12
createSumFunc 建立求和函式 33 6
createTriFunc 建立三角函式 15 3
createOpFunc 建立符號函式 13 3
createAddSubFunc 建立加減法函式 29 7
createPowerFunc 建立指數函式 31 7
createMulFUnc 建立乘法函式 33 8
createVarFunc 建立變元 10 2
createNumFunc 建立常元 10 2
FuncLib addFunction 新增自定義函式形式 5 1
createCustomFunc 建立自定義函式呼叫 35 7
getRealParamStrings 解析實參字串 45 12
Output duplicate 複製 7 2
getString 獲得化簡字串 26 7
power 乘方 15 3
mul 乘法 22 6
sub 減法 5 1
add 加法 19 5
negate 取負 8 2
addTri 新增三角函式 10 3
addNum 新增常數 4 1
addVar 新增變數 4 1
isNumFactor 是否是常量因子 7 2
isVarFactor 是否是變數因子 17 5
Term duplicate 複製 10 3
getString 獲得化簡字串 53 16
mul 乘法 30 7
isNumber 是否是常數 9 3
isPower 是否只含有變數指數 9 3
isSimilar 是否是同類項 6 2
StrUtil noBlank 去除空白 6 2
noBracket 去除兩端對應的括號 29 8

2.2 發現的bug位置(第二、三次作業)

  • 筆誤
    在 FuncFactory.createPowerFunc() 方法中,使用到了很大的迴圈,迴圈中的一個判斷條件寫錯。此處程式碼31行,是同類方法行數第二長的。
  • 超時
    在 FuncFactory.createFunc() 中沒有特判,導致遞迴呼叫超時。該方法47行,是該類方法中最長的,該方法的CC為12,也是該類方法中最大的。這導致這個函式不好除錯,而且一旦出錯會影響到很多地方。
  • 重寫錯誤
    在 MulFunc.duplicate() 方法中,重寫Func.duplicate(),忘記遞迴呼叫乘法兩邊的複製方法,導致該方法複製不完全,沒有達到深拷貝的效果。

2.3 結構圖

  • 第一次作業

  • 第二、三次作業

3. 測試策略

因為時間所限,只有第一次作業做了比較完全的測試。因為整個結構是比較有規律的,二、三次作業根據1.1的易錯點進行測試,即可基本保證不出問題。完全測試基本上是根據形式化表述,從底向上,逐層測試。比如先測試各種因子對不對,然後構造項進行測試,最後構造表示式進行測試。

因為時間所限,我沒有具體針對程式碼進行互測,只是隨意交了一些資料進行測試,當然也沒有查到別人的bug。我認為,最大的問題可能就是物件深拷貝,其次是函式多次呼叫和迴圈呼叫的問題。如果這兩個點都是正確的,我相信其他地方也不會有很大問題。

4. 設計體驗

4.1 架構迭代

因為第一次作業只有多項式,所以我採用了很簡單的方法解析,專門針對多項式。然後根據助教的提醒,後期的作業需要更大範圍的抽象,所以只能重構了。第二次作業因為時間不夠,加上整個架構全部要重寫,所以作業效果並不好。但是架構設計好了以後,為第三次作業帶來了很大的方便。第三次作業在第二次的基礎上,修改行數估計只有二三十行,修改時間估計一兩個小時。而且在強測和互測也沒有什麼錯誤。我認為只要把易錯點都檢查過了,就不會有什麼問題。

4.2 心得體會

第一單元是進入OO課程的第一次訓練。通過三次作業,我逐步地提高設計的抽象程度,以應對增加的需求。我原先認為,第一次作業和第二、三次的銜接不夠,針對第一次作業的設計結構到後面幾乎都要重構,否則會變得非常複雜。但是現在我認為這也不是什麼問題。因為之後確實有可能會提出各種需求,而這是在之前的作業中不能預計的。所有的遠見都是有限的,不可能考慮到所有的情況。我個人認為,程式首先要滿足當前的需求和效能,其次才是預留未來的需求。我覺得要抓住主要矛盾和主要方面,優先滿足當前需求,該重構時就重構,我聽說unix還是linux這麼龐大的系統都重寫了幾次。

關於作業量和難度,我因為之前看過一些Java的教程,所以感覺難度不是很大,只是作業量有點多,需要平衡這個課和其他的課的時間分配。但是我也聽說有的同學感覺有些難度,可能有一次作業做得不是特別好。如果我之前沒有看過相關知識,可能也會感覺比較吃力。我覺得這實在是沒辦法的事情,想要用好一門語言,至少要知道語法和常用用法。在pre中有比較好的練習,因為pre不是強制的任務,可能有些同學就沒做了。