是一個標題
211614269 林凱 211601233 張康凌
一、預估與實際
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | ||
• Estimate | • 估計這個任務需要多少時間 | 10 | 10 |
Development | 開發 | ||
• Analysis | • 需求分析 (包括學習新技術) | 60 | 120 |
• Design Spec | • 生成設計文檔 | 30 | 30 |
• Design Review | • 設計復審 | 30 | 30 |
• Coding Standard | • 代碼規范 (為目前的開發制定合適的規范) | 10 | 10 |
• Design | • 具體設計 | 60 | 60 |
• Coding | • 具體編碼 | 240 | 400 |
• Test | • 測試(自我測試,修改代碼,提交修改) | 30 | 30 |
Reporting | 報告 | ||
• Test Repor | • 測試報告 | 30 | 30 |
• Size Measurement | • 計算工作量 | 10 | 20 |
• Postmortem & Process Improvement Plan | • 事后總結, 並提出過程改進計划 | 60 | 60 |
合計 | 570 | 800 |
二、需求分析
我通過查詢教學大綱及研究相關試卷的方式了解到,小學三級數學有如下的幾個特點:
- 特點1
- 減法的差應該大於0
- 特點2
- 除數要不等於0
- 特點3
- 除法結果不應該有余數
- 特點4
- 減法的差應該大於0
- 特點5
- 運算符2-4個
- 特點6
- 需要括號
經過分析,我認為,這個程序應當:
- 要保證減法時的差不為負數
- 除法時要保證被除數不為0且計算結果沒有余數
- 隨機出現合適的括號(由於實在想不到很好的方式實現,出題模塊暫時沒有此功能,計算的模塊支持帶括號式子的計算)
三、設計
1. 設計思路
-
這個程序有兩個類
- MathExam負責生成題目
- Calculate類專門用來計算表達式的結果
-
算法的關鍵是什么?
- 調度場算法
- 定義兩個棧,一個棧叫做保存運算符記為op,另一個棧保存最終的表達式記為rpn。
- 數字直接入op棧
- op棧頂若是(則無條件入棧
- 運算符要與op棧頂比較,優先級大則入棧,小於或等於則op出棧后再入棧
- 逆波蘭式求值
- 定義一個棧
- 從左到右掃描逆波蘭式
- 讀到一個數字時就將它壓入棧中
- 讀到一個運算符時,就從棧中彈出兩個數字,並將該運算符作用於這兩個數字,然后將計算結果再壓入棧中
- 逆波蘭式讀取完畢時,棧中剩下的就是最終結果
- 生成合適的隨機數
- (int) (min + Math.random() * (max - min + 1)) 可生成min-max范圍內的數;
- 調度場算法
2. 實現方案
- 准備工作:先在Github上創建倉庫,克隆到本地
- 技術關鍵點:梳理一下設計思路,可能遇到哪些技術關鍵點
- 如何將調度場算法運用到該題中去
- 隨機數的使用,本程序需要大量的使用Math.random()方法
- 全局靜態變量的使用,本程序需要多個靜態函數共享數據
- ArrayList數組的使用,用該數組存儲題目及答案的字符串,相比普通數組,該數組可以動態增加空間
四、編碼
請說明你如何按照設計思路進行編碼,並記錄你在開發中遇到的問題,與解決過程
1. 調試日志
- 在代碼的什么位置,代碼出現了什么問題,問題會導致什么結果,怎么解決的
- 在使用調度場算法將題目轉換成逆波蘭式時出現了問題,因為題目的數字可能是個位也可能是兩位,直接通過下標截取字符串的方法明顯不合適。最后我決定寫一個方法,將題目中的符號及數值按順序存放至字符串數組中,使用的時候遍歷數組取出即可。
- 在出題時,有可能出現除法有余數的情況,導致題目不符合要求。我的做法是在生成除號時,將前一個被除數取出,同時隨機出被除數,若不能整除,則一直循環隨機出被除數,最后一定能保證不會有余數。
2. 關鍵代碼
請展示一段程序的關鍵代碼,並解釋代碼的作用
//返回符號優先級
public static int priority(String str) {
switch (str) {
case "(":
return 0;
case "+":
case "-":
return 1;
case "*":
case "/":
return 2;
default:
return -1;
}
}
// 轉換成逆波蘭式
public static void reversePolishNotation() {
for (int i = 0; i < slen; i++) {
//scut字符串數組按順序存放了題目的數值及符號
if (!(scut[i].equals("+") || scut[i].equals("-") || scut[i].equals("*") || scut[i].equals("/") || scut[i].equals("(") || scut[i].equals(")"))) {
srpn.push(scut[i]);
} else {
if (soperators.isEmpty() || scut[i].equals("(")) {
soperators.push(scut[i]);
} else {
if (priority(scut[i]) > priority(soperators.peek())) {
soperators.push(scut[i]);
} else {
if(scut[i].equals(")")) {
while (!soperators.peek().equals("(")) {
srpn.push(soperators.pop());
}
soperators.pop();
}else {
while ((!soperators.isEmpty()) && (priority(soperators.peek()) >= priority(scut[i]))) {
srpn.push(soperators.pop());
}
soperators.push(scut[i]);
}
}
}
}
}
//此時原表達式已掃描完畢,將符號棧里剩余的符號全部存至表達式棧
while (!soperators.isEmpty()) {
srpn.push(soperators.pop());
}
}
- 該段代碼將原表達式通過調度場算法轉換成了逆波蘭式
3. 代碼規范
請給出本次實驗使用的代碼規范:
- 第一條、代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。
- 第二條、類名使用UpperCamelCase風格
- 第三條、方法名、參數名、成員變量、局部變量都統一使用 lowerCamelCase 風格,必須遵循駝峰形式。
- 第四條、方法名、參數名、成員變量、局部變量都統一使用 lowerCamelCase 風格,必須遵循駝峰形式。
- 第五條、靜態變量(static)前面加上小寫s。
4. 結對過程
- 先互相溝通,對於要求進行分析,提出合適的解決思想
- 我先設計出Calculate類專門負責計算傳入式子的結果,該類主要用到了調度場算法及逆波蘭式的計算
- 張康凌同學則寫出了生成三年級題目的基本算法
- 我則繼續在出題算法上進一步修改,使其更符合題目的要求
- 實現基本功能后則一起進行相應的測試
五、測試
請思考並記錄你認為必要的測試點,並記錄測試用例與測試結果
測試項目 | 測試語句 | 預期結果 | 測試結果 |
---|---|---|---|
不輸入任何參數 | java MathExam4269 | 輸入錯誤 | 輸入錯誤 |
輸入一個參數 | java MathExam4269 20 | 輸入錯誤 | 輸入錯誤 |
輸入二個參數 | java MathExam4269 -n 20 | 輸入錯誤 | 輸入錯誤 |
輸入4個參數且題數在前 | java MathExam4269 -n 20 -grade 2 | 出20道二年級題目 | 出20道二年級題目 |
輸入4個參數且年級在前 | java MathExam4269 -grade 2 -n 20 | 出20道二年級題目 | 出20道二年級題目 |
輸入4個參數且題數過多 | java MathExam4269 -grade 2 -n 1000 | 輸入錯誤 | 輸入錯誤 |
六、總結
-
吸取上次個人作業的教訓,開始時要先設計好相應的框架,思路
-
編程是一件非常需要細心的事情,不僅要實現好功能,后續也要考慮對代碼進行一些優化
-
每次修改程序,都應該進行回滾測試,確保新增功能不會影響原功能的實現,調試程序時也要考慮到盡可能多的情況
-
對於結對編程
-
設計時應與隊友一起進行,自己和隊友都要理解相應的思路,這樣無論誰做駕駛員時都能理解代碼,避免因為沒有溝通充分導致后續編程出現問題
-
結對編程時雙方都要輪流做駕駛員,另一方也需要做好領航者,沒有任何一方可以偷懶
-
結對編程的好處很多,碰到問題時可以一起討論,觀察代碼的一方也容易發現編寫代碼者沒發現的錯誤
-