1. 程式人生 > >MathExamV2.0四則混合運算計算題生成器

MathExamV2.0四則混合運算計算題生成器

理解 防止 逆波蘭 因此 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四則混合運算計算題生成器