MathExamV2.0四則混合運算計算題生成器
阿新 • • 發佈:2018-09-19
理解 防止 逆波蘭 因此 bubuko 關鍵點 ima 創建倉庫 調試日誌
MathExamV2.0四則混合運算計算題生成器----211606360 丁培暉 211606343 楊宇瀟
一、預估與實際
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
? Estimate | ? 估計這個任務需要多少時間 | 60 | 100 |
Development | 開發 | ||
? Analysis | ? 需求分析 (包括學習新技術) | 360 | 440 |
? Design Spec | ? 生成設計文檔 | 20 | 20 |
? Design Review | ? 設計復審 | 30 | 40 |
? Coding Standard | ? 代碼規範 (為目前的開發制定合適的規範) | 30 | 50 |
? Design | ? 具體設計 | 120 | 180 |
? Coding | ? 具體編碼 | 350 | 550 |
? Code Review | ? 代碼復審 | 90 | 120 |
? Test | ? 測試(自我測試,修改代碼,提交修改) | 30 | 45 |
Reporting | 報告 | ||
? Test Repor | ? 測試報告 | 30 | 30 |
? Size Measurement | ? 計算工作量 | 30 | 35 |
? Postmortem & Process Improvement Plan | ? 事後總結, 並提出過程改進計劃 | 60 | 60 |
合計 | 1210 | 1670 |
二、需求分析
我通過在網絡搜索的方式了解到,小學三年級數學有如下的幾個特點:
- 特點1:式子的運算無法得出負數。
- 特點2:數字範圍增加,可以得出上千的結果。
- 特點3:除數不能為0。
- 特點4:在一個式子內會使用多個運算符號。
- 特點5:清楚加減乘除算式的優先級,並且明白括號的意思。
- 特點6:式子的結果大小限定在10000以內。
經過分析,我認為,這個程序應當:
- 使用的數字大小應為[0,1000)
- 並且判斷除數的數字大小應為[1,1001)
- 生成的符號至少兩個,並且至少兩種。
三、設計
1. 設計思路
這一次的作業,由於需要使用逆波蘭表達式,因此我們使用了和上次作業不同的輸入文本的方式,這一次使用的是將字符串轉為byte[],然後輸入文本的方式。
我們還分出了多中方法體以及構造函數,在主函數main()裏調用構造函數,在構造函數內調用各個方法體。(第一次作業只建立了一個方法體,並且在主函數調用,顯得代碼臃腫,因為將所有的算法都集中在一個方法體內,因此作出改變。)
- 根據題目要求,傳入的參數有-n和-grade,因此使用if()判斷了參數的-n和-grade的順序,並建立了一個judge()的方法體,使用正則表達式判斷參數的格式。
- 數字需要隨機得出,因此需要建立隨機數,隨機數有多種。
- 隨機出來的式子需要寫入文本,因此創建了一個TxT()的方法,用於創建文本並將式子輸入文本當中,使用String數組進行存儲式子,並用Byte[]轉換,最後輸入文本。
- 答案需要的格式為“等式+答案”,而且不需要小數以及負數,因此將隨機數類型定義為整型,並在最後判斷答案是否符合要求。
- 判斷加減乘除需要使用到優先級,因此創建了一個Level方法,返回值為0,1,2,用於判斷符號的優先級。
- 因為三年級的式子為四則混合運算,因此使用中序表達式以及逆波蘭,將String數組裏的式子轉化為線性表,最終調用reckon()方法計算線性表內的式子。並返回最終結果。
2. 實現方案
- 準備工作:先在Github上創建倉庫,克隆到本地,並邀請結伴對象,將代碼提交到一個倉庫內。
- 技術關鍵點:
- 四則混合運算式子以及括號的判斷,需要使用到優先級,因此建立一個關於判斷優先級的Level()的方法。
- 使用隨機數Random( )時,需要註意隨機數生成範圍,比如除數不能為0。
- 當式子內有減法,需要判斷式子的答案是否大於等於0。
- 使用String數組將式子轉換為Byte [] ,並將其全部輸入文本。
- 使用正則表達式、Pattern、Matcher判斷輸入參數是否達標,不達標則提示輸入錯誤,並結束程序。
- 使用 DateFormat( )獲取系統當前時間。
四、編碼
請說明你如何按照設計思路進行編碼,並記錄你在開發中遇到的問題,與解決過程
1. 調試日誌
記錄編碼調試的日誌,請記錄下開發過程中的 debug 歷程
- 數據傳入棧中,會出現空指針,導致程序不能運行。
解決方案:檢查隨機數的生成,看隨機數是否和判斷條件相符合。- 命令行輸入格式不對,會出錯。
解決方案:通過字符匹配,如果輸出格式不對,報錯結束進程。- 除法計算,除數生成了0.
解決方案:在生成隨機數時+1.- 輸入題目個數輸入過大,或者輸入不是數字,會報錯。
解決方案:用正則表達式來判斷傳入參數是否合理。- 使用逆波蘭計算方法不能正確計算。
解決方案:檢查傳入參數是否正確,寫的方法是否正確,使用方法是否正確。
2. 關鍵代碼
請展示一段程序的關鍵代碼,並解釋代碼的作用
- 通過main函數中String[] args的參數,將其傳入構造函數內,並在構造函數中判斷出-n與-grade的順序,並將相應的年級和題目數量傳入judge()方法體內,判斷輸入的格式是否正確。
- 通過了參數格式判斷後,將參數傳入calculate()和TxT()的方法內。
- 在calculate()判斷傳入的年級大小後,根據年級大小調用calculate_1(),calculate_2(),calculate_3()。分別對應一年級、二年級、三年級的式子生成方法。
- 使用random()隨機生成數字。
- 這一段是調用三年級的式子生成方法。
private void calculate_3(int count) {
int i=0;
while(i<count){
int symbol_number=(int)(Math.random()*5+2); //隨機生成數字,用於判斷符號個數
int number_1=(int)(Math.random()*1000); //隨機生成第一個數字
int number_2=(int)(Math.random()*1000+1); //隨機生成第二個數字
int number_3=(int)(Math.random()*1000+1); //隨機生成第三個數字
int symbol_1=(int)(Math.random()*4); //隨機生成第一個符號
int symbol_2=(int)(Math.random()*4); //隨機生成第二個符號
if(symbol_number == 2) {
if(symbol_1==symbol_2) //保證至少有兩種運算符號。
{continue;}
if(Level(SymB[symbol_1])<Level(SymB[symbol_2])) { //比較第一個符號和第二個符號的優先級,並添加括號。
QT_1[i]="("+number_1+SymB[symbol_1]+number_2+")"+SymB[symbol_2]+number_3;
QT[i]="( "+number_1+" "+SymB[symbol_1]+" "+ number_2 +" ) "+SymB[symbol_2] +" " +number_3;
}
else if(Level(SymB[symbol_1])>Level(SymB[symbol_2])) {
QT_1[i]=+number_1+SymB[symbol_1]+"(" + number_2 +SymB[symbol_2]+number_3+")";
QT[i]=number_1+" "+SymB[symbol_1]+" ( "+ number_2 +" "+SymB[symbol_2] +number_3+" ) ";
}
else {
QT_1[i]=+number_1+SymB[symbol_1]+number_2+SymB[symbol_2]+number_3;
QT[i]=number_1+" "+SymB[symbol_1]+" "+ number_2 +" "+SymB[symbol_2] +" "+number_3;
}
}
//判斷運算符為兩個之後,開始判斷符號優先級,並在式子裏添加括號。
else if(symbol_number == 3) {
if(symbol_1==symbol_2) //保證式子內至少有兩種運算符號
{continue;}
int number_4=(int)(Math.random()*1000+1);
int symbol_3=(int)(Math.random()*4);
if(Level(SymB[symbol_1])<Level(SymB[symbol_2])) {
QT_1[i]="("+number_1+SymB[symbol_1]+number_2+")"+SymB[symbol_2]+number_3+SymB[symbol_3]+number_4;
QT[i]="( "+number_1+" "+SymB[symbol_1]+" "+ number_2 +" ) "+SymB[symbol_2] +" " +number_3+" "+SymB[symbol_3]+" "+number_4;
}
else if(Level(SymB[symbol_1])>Level(SymB[symbol_2])) {
QT_1[i]=+number_1+SymB[symbol_1]+"(" + number_2 +SymB[symbol_2]+number_3+")"+SymB[symbol_3]+number_4;
QT[i]=number_1+" "+SymB[symbol_1]+" ( "+ number_2 +" "+SymB[symbol_2] +number_3+" ) "+SymB[symbol_3]+" "+number_4;
}
else if(Level(SymB[symbol_2])>Level(SymB[symbol_3])) {
QT_1[i]=+number_1+SymB[symbol_1]+ number_2 +SymB[symbol_2]+"("+number_3 +SymB[symbol_3]+number_4+")";
QT[i]=number_1+" "+SymB[symbol_1]+" "+ number_2 +" "+SymB[symbol_2] +" ( " +number_3+" "+SymB[symbol_3]+" "+number_4+" ) ";
}
else {
QT_1[i]=+number_1+SymB[symbol_1]+number_2+SymB[symbol_2]+number_3+SymB[symbol_3]+number_4;
QT[i]=number_1+" "+SymB[symbol_1]+" "+ number_2 +" "+SymB[symbol_2] +" "+number_3+" "+SymB[symbol_3]+" "+number_4;
}
}
//判斷運算符為三個之後,開始判斷符號優先級,並在式子裏添加括號。
else if(symbol_number == 4) {
if(symbol_1==symbol_2) //保證式子至少有兩種運算符號
{continue;}
int number_4=(int)(Math.random()*1000+1);
int number_5=(int)(Math.random()*1000+1);
int symbol_3=(int)(Math.random()*4);
int symbol_4=(int)(Math.random()*4);
if(Level(SymB[symbol_1])<Level(SymB[symbol_2])) {
QT_1[i]="("+number_1+SymB[symbol_1]+number_2+")"+SymB[symbol_2]+number_3+SymB[symbol_3]+number_4+SymB[symbol_4]+number_5;
QT[i]="( "+number_1+" "+SymB[symbol_1]+" "+ number_2 +" ) "+SymB[symbol_2] +" " +number_3+" "+SymB[symbol_3]+" "+number_4+" "+SymB[symbol_4]+" "+number_5;
}
else if(Level(SymB[symbol_1])>Level(SymB[symbol_2])) {
QT_1[i]=number_1+SymB[symbol_1]+"("+number_2+SymB[symbol_2]+number_3+")"+SymB[symbol_3]+number_4+SymB[symbol_4]+number_5;
QT[i]=number_1+" "+SymB[symbol_1]+" ( "+ number_2 +" "+SymB[symbol_2]+" "+number_3+" ) "+SymB[symbol_3]+" "+number_4+" "+SymB[symbol_4]+" "+number_5;
}
else if(Level(SymB[symbol_2])>Level(SymB[symbol_3])) {
QT_1[i]=number_1+SymB[symbol_1]+number_2+SymB[symbol_2]+"("+number_3+SymB[symbol_3]+number_4+")"+SymB[symbol_4]+number_5;
QT[i]=number_1+" "+SymB[symbol_1]+" "+ number_2 + " "+SymB[symbol_2]+" ( "+number_3+" "+SymB[symbol_3]+" "+number_4+" ) "+" "+SymB[symbol_4]+" "+number_5;
}
else if(Level(SymB[symbol_3])>Level(SymB[symbol_4])) {
QT_1[i]=number_1+SymB[symbol_1]+number_2+SymB[symbol_2]+number_3+SymB[symbol_3]+"("+number_4+SymB[symbol_4]+number_5+")";
QT[i]=number_1+" "+SymB[symbol_1]+" "+ number_2 + " "+SymB[symbol_2]+" "+number_3+" "+SymB[symbol_3]+" ( "+number_4+" "+SymB[symbol_4]+" "+number_5+" )";
}
else {
QT_1[i]=number_1+SymB[symbol_1]+ number_2+SymB[symbol_2]+number_3+SymB[symbol_3]+number_4+SymB[symbol_4]+number_5;
QT[i]=number_1+" "+SymB[symbol_1]+" "+ number_2 +" "+SymB[symbol_2] +" " +number_3+" "+SymB[symbol_3] +" "+number_4+" "+SymB[symbol_4]+" "+number_5;
}
}
//判斷運算符為四個之後,開始判斷符號優先級,並在式子裏添加括號。
else {continue;} //當symbol_number隨機出不需要的數字時,結束本次循環,重新開始新的循環。主要防止出現空指針。
List<String> rec= toInfixExpression(QT_1[i]); //調用中序表達式。
AS[i]=QT[i]+" = "+reckon(rec);
if(reckon(rec)<0 || reckon(rec)>10000) //當數字的結果大小為負數或者大於10000,結束本次循環,重新生成式子。
{continue;}
i++;
}
}
3. 代碼規範
請給出本次實驗使用的代碼規範:
- 第一條:代碼中的變量名不能以下劃線開始也不能以下劃線結束。
- 第二條:代碼中的命名嚴禁使用拼音與英文混合,更不許使用中文命名。
- 第三條:常量命名全部大寫,單詞鍵用下劃線隔開,力求語義表達完整,不要嫌名字長。
- 第四條:類型與中括號緊挨相連來定義數組。
- 第五條:杜絕完全不規範的縮寫,避免忘文不知義。
- 第六條:第四條:不要使用一個常量類維護所有常量,按常量功能進行歸類,分開維護。並人工檢查代碼是否符合規範
五、測試
請思考並記錄你認為必要的測試點,並記錄測試用例與測試結果
測試編號 | 具體輸入 | 預期結果 | 實際結果 |
---|---|---|---|
1 | -n 10 -grade 1 | 輸出10題一年級題目 | 符合預期 |
2 | -n -5 -grade 1 | 提示錯誤,停止運行 | 符合預期 |
3 | -n 10 -grade 3 | 輸出10題三年級題目 | 符合預期 |
4 | -grade 3 -n 10 | 輸出10題三年級題目 | 符合預期 |
5 | A N | 提示錯誤,停止運行 | 符合預期 |
6 | -n 10.3 -grade 3.0 | 提示錯誤,停止運行 | 符合預期 |
7 | -n 99999 -grade 4 | 提示錯誤,停止運行 | 符合預期 |
六、總結
請總結過程中的教訓和經驗,思考
- 在這一次的作業中,由於使用了逆波蘭以及中序表達式,並且還有堆棧和線性表的運用,由於基礎薄弱,我和結伴對象在寫代碼之前,花費了大量的時間去查看相關知識,並查看相關的博客代碼,詢問班裏的大神,最終成功理解了其運算方式。
- 結對作業和之前的個人作業並不一樣,因為是兩個人一起敲代碼,並且我們的想法以及思路都不同,因此每敲一行,都會對同伴說出關於這一行代碼我要用它來做什麽,這一行代碼的用處。同時,也會提出建議,一種不同的想法,最後撮合而成,出發點不一樣,想法不一樣。導致了我們可以對代碼更好的優化,畢竟當局者迷,旁觀者清,有的不必要的代碼,確實在旁觀看的人會看的更加清晰,並能指出一些錯誤邏輯。
- 有關於調用逆波蘭和中序表達式兩種方法的時候,我不只一次出現了空指針的錯誤,原因就是if()判斷隨機數生成的時候,並沒有把所有可能列出來,導致出現了if()判斷之外的數字,從而導致了需要接收式子的String數組 QT_1並沒有獲得式子,將一個空數組傳入中序表達式內,最終導致程序報錯,出現空指針的問題。
- 在我無法判斷出錯誤地點之時,同伴的幾句話,可能是無心,也可能是有意,卻能驚醒我這個夢中人,使我發現錯誤的地方,同時進行修改,最後得出最終結果。
- 結對編程相比於個人作業,多了頭腦風暴和靈感沖撞,在一次次的沖突碰撞之下,在一次次克服難題之下,我們磕磕碰碰,修修改改,最終提交了代碼,完成了本次作業。
- 但是這次的代碼依舊有許多不足,我使用了優先級進行判斷,然後逐一添加括號,在我的想法中,其實不只是兩個數字,甚至可以括三個、四個數字。然而想法是美好的,但真正寫出來卻只能實現一個括號,只能括兩個數字。
- 代碼仍具有局限性,我和結伴對象雖然討論過,但卻沒有得出能夠符合我們想法的代碼,因此只能礙於時限,草草提交了本次作業。代碼依舊有著許多不足,能夠改進的地方還有很多,相信在下一次的作業中,我能夠獲得新的想法,同時改進本次的不足,逐一完善我的代碼。
- 結對編程很有趣,兩個人的想法沖突,兩個人的意見沖突,不僅能給枯燥的編程添加點調味劑,同時也能因為靈感沖突、結合產生新的靈感,可以說是每寫出一種想法就能蹦出另外一種更大膽的想法,讓人忍不住去嘗試,雖然結果往往都是錯的,不過說實話,真理不就是在眾多的錯誤中誕生的嗎?
MathExamV2.0四則混合運算計算題生成器