本項目Github地址:https://github.com/Rollsom/MyApp 項目簡介: 實現一個自動生成小學四則運算題目的命令行程式。 項目相關要求: 使用 -n 參數控制生成題目的個數 使用 -r 參數控制題目中數值(自然數、真分數和真分數分母)的範圍,例如:Myapp.exe -r ...
本項目Github地址:https://github.com/Rollsom/MyApp
項目簡介:
實現一個自動生成小學四則運算題目的命令行程式。
項目相關要求:
使用 -n 參數控制生成題目的個數
使用 -r 參數控制題目中數值(自然數、真分數和真分數分母)的範圍,例如:Myapp.exe -r 10
將生成10以內(不包括10)的四則運算題目。該參數可以設置為1或其他自然數。該參數必須給定,否則程式報錯並給出幫助信息。
3. 生成的題目中計算過程不能產生負數,也就是說算術表達式中如果存在形如e1 − e2的子表達式,那麼e1 ≥ e2。
4. 生成的題目中如果存在形如e1 ÷ e2的子表達式,那麼其結果應是真分數。
5. 每道題目中出現的運算符個數不超過3個。
6. 程式一次運行生成的題目不能重覆,即任何兩道題目不能通過有限次交換+和×左右的算術表達式變換為同一道題目。
生成的題目存入執行程式的當前目錄下的Exercises.txt文件,格式如下:
1. 四則運算題目1
2. 四則運算題目2
……
其中真分數在輸入輸出時採用如下格式,真分數五分之三表示為3/5,真分數二又八分之三表示為2’3/8。
7. 在生成題目的同時,計算出所有題目的答案,並存入執行程式的當前目錄下的Answers.txt文件,格式如下:
1. 答案1
2. 答案2
8. 程式應能支持一萬道題目的生成。
9. 程式支持對給定的題目文件和答案文件,判定答案中的對錯併進行數量統計,輸入參數如下:
Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt
統計結果輸出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
解題思路:
大致思路為:先隨機生成若幹個子表達式(表達式內操作數不超過2個,若操作數為2個則加上括弧),根據數字特征生成合適的操作符,隨後根據子表達式的總值來生成表達式之間的運算符號,期間去除多餘的括弧,並且將小數換成真分數。從而生成一個符合要求的表達式。
查重則使用二叉樹,在生成子表達式的過程中構建二叉樹,查重就變成了兩個二叉樹是否相等的問題。
最後輸出文件以及正誤判斷只需要用到文件的輸入輸出流以及簡單的字元串判斷方法即可。
代碼結構:
Expression類:當做數據結構來用,裡面主要設置一些表達式裡面的一些參數方便編程。
Data類:數據的屬性,主要用來處理真分數的轉換,整數及真分數的運算,生成符合輸入輸出的數字表達。
CQ類:生成子表達式及組合表達式,生成結果。
ExpressionArray類:用來處理生成的表達式數組,主要用來查重以及存儲生成的每一個表達式。
Tree類:子表達式附加的類,用來輔助查重。
Saveanswer類:將生成的表達式輸出到文件中,以及統計正誤數量。
關鍵代碼說明:
生成子表達式:
public void CQQ(Expression e,int Range) {//創造表達式 e.flag1 = true; Random r = new Random(); if(this.CurrentOperateNum==3||this.CurrentOperateNum==0) { e.Operate1.CreatNumble(Range); this.CurrentOperateNum += 1; e.value = e.Operate1.ExpressionValue; e.Result = e.Operate1; this.ExpressionNum += 1; e.flag1=true; e.Node.value = e.Operate1.ExpressionValue; } else if(this.CurrentOperateNum==4) return ; else { e.Operate1.CreatNumble(Range); this.ExpressionNum += 1; e.flag1 = true; if(r.nextInt(2)==1) //生成兩個操作數 { e.flag = true; e.Operate2.CreatNumble(Range); this.CurrentOperateNum += 2; switch(r.nextInt(4)) { case 0 : e.value = e.Operate1.ExpressionValue + e.Operate2.ExpressionValue; e.Operator = '+'; break; case 1 : while(e.Operate1.ExpressionValue < e.Operate2.ExpressionValue) { e.Operate1.CreatNumble(Range); e.Operate2.CreatNumble(Range); } e.value = e.Operate1.ExpressionValue - e.Operate2.ExpressionValue; e.Operator = '-'; break; case 2 : e.value = e.Operate1.ExpressionValue * e.Operate2.ExpressionValue; e.Operator = '*'; break; case 3 : e.value = e.Operate1.ExpressionValue / e.Operate2.ExpressionValue; e.Operator = '÷'; break; } e.Result = e.Result.Carculate(e, e.Operate1, e.Operate2, e.Operator); e.Node.e1.value = e.Operate1.ExpressionValue; e.Node.e2.value = e.Operate2.ExpressionValue; e.Node.value = e.value; } else { this.CurrentOperateNum += 1; e.value = e.Operate1.ExpressionValue; e.Result = e.Operate1; e.Node.value = e.Operate1.ExpressionValue; } }
組建已經生成的表達式:
public String GroupSonExpression(Expression e1,Expression e2,Expression father) { int Operate; Random r = new Random(); Operate = r.nextInt(4); while((e1.value<e2.value&&Operate==1)||(e2.value==0&&Operate==3)) Operate = r.nextInt(3); if(e1.flag1==true&&e2.flag1==true) { switch(Operate) { case 0 : father.Operator = '+';father.value = e1.value + e2.value; if(e1.flag==true) { e1.flag = false; e1.GetExpression(); } if(e2.Operator=='*'||e2.Operator=='÷') { e2.flag = false; e2.GetExpression(); } break; case 1 : father.Operator = '-';father.value = e1.value - e2.value; if(e1.flag==true) { e1.flag = false; e1.GetExpression(); } if(e2.Operator=='*'||e2.Operator=='÷') { e2.flag = false; e2.GetExpression(); } break; case 2 : father.Operator = '*';father.value = e1.value * e2.value; if(e1.Operator=='*'||e1.Operator=='÷') {e1.flag = false; e1.GetExpression();} break; case 3 :father.Operator = '÷';father.value = e1.value / e2.value; if(e1.Operator=='*'||e1.Operator=='÷') {e1.flag = false; e1.GetExpression();} break; } father.Result = father.Result.Carculate(father, e1.Result, e2.Result, father.Operator); father.SonExpression = e1.Expression + father.Operator + e2.Expression; father.Expression = "("+ father.SonExpression +")"; father.flag1 = true; father.Node.value = father.value; father.Node.e1 = e1.Node; father.Node.e2 = e2.Node; } else if(e2.flag1==false&&e1.flag1==true) { father.value = e1.value; father.Operate1 = e1.Operate1; father.Operate2 = e1.Operate2; father.Expression = e1.Operate1.Fraction; father.Operator = e1.Operator; father.Result = e1.Result; father.flag = e1.flag; father.Result.ExpressionValue = e1.Result.ExpressionValue; father.flag1 = true; if(e1.Operator==' ') father.SonExpression = e1.Expression; else father.SonExpression = e1.Operate1.Fraction+ father.Operator + e1.Operate2.Fraction; father.Node = e1.Node; } else father = e2; return father.Expression; }
真分數的各種運算:
public Data Carculate(Expression e,Data d1,Data d2,char Operator) { Data d = new Data(); switch(Operator) { case '+':d.Molecule = d1.Molecule * d2.Deno + d2.Molecule * d1.Deno; d.Deno = d1.Deno * d2.Deno; d.integer = d1.integer + d2.integer; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; case '-': d.Molecule = d2.Deno * (d1.integer * d1.Deno+d1.Molecule) - d1.Deno * (d2.integer * d2.Deno + d2.Molecule); //d1.Molecule * d2.Deno - d2.Molecule * d1.Deno; d.Deno = d1.Deno * d2.Deno; d.integer = 0; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; case '*': d.Molecule = (d1.Molecule+d1.integer * d1.Deno) * (d2.Molecule + d2.integer * d2.Deno); d.Deno = d1.Deno * d2.Deno; d.integer = 0; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; case '÷':d.Molecule = (d1.Molecule+d1.integer * d1.Deno) * d2.Deno; d.Deno = d1.Deno * (d2.Molecule + d2.integer * d2.Deno); d.integer = 0; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; default : System.out.println("計算出錯");break; } return d; }
查重:
boolean IFequal(Tree T1,Tree T2) { if(T1==null&&T2==null) return true; else if(T1==null||T2==null) return false; else if(T1.value!=T2.value||T1.Operator!=T2.Operator) return false; else if((IFequal(T1.e1,T2.e1)&&IFequal(T1.e2,T2.e2))||(IFequal(T1.e2,T2.e1)&&IFequal(T1.e1,T2.e2))) return true; return false; }
運行測試舉例:
生成10000條表達式並且操作數範圍為20,輸出到.txt文件上面。
測試比較答案對錯:
psp:
PSP2.1 |
Personal Software Process Stages |
預估耗時(分鐘) |
實際耗時(分鐘) |
Planning |
計劃 |
60 |
120 |
· Estimate |
· 估計這個任務需要多少時間 |
60 |
120 |
Development |
開發 |
1800 |
2140 |
· Analysis |
· 需求分析 (包括學習新技術) |
60 |
60 |
· Design Spec |
· 生成設計文檔 |
60 |
90 |
· Design Review |
· 設計覆審 (和同事審核設計文檔) |
20 |
30 |
· Coding Standard |
· 代碼規範 (為目前的開發制定合適的規範) |
40 |
40 |
· Design |
· 具體設計 |
120 |
180 |
· Coding |
· 具體編碼 |
720 |
960 |
· Code Review |
· 代碼覆審 |
60 |
60 |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
720 |
720 |
Reporting |
報告 |
40 |
40 |
· Test Report |
· 測試報告 |
15 |
15 |
· Size Measurement |
· 計算工作量 |
0 |
0 |
· Postmortem & Process Improvement Plan |
· 事後總結, 並提出過程改進計劃 |
25 |
25 |
合計 |
|
1900 |
2300 |
代碼覆蓋率:
執行-n 與 -r 時:
執行-e 與 -a 時:
項目總結:
本次項目比上一次的項目的難度要高許多,製作時間比較緊張。在這一次編程中,我實現功能需要用到的類比較多,類比較簡單所以多而且繁瑣,相互又有聯繫,所以出bug的時候總是會卡很久。而且這次隨我個人而言也是一次很大的挑戰,我平時沒有學習技術,只有要做項目的時候才去學,是非常痛苦的事情,這痛苦的經歷也告訴了我平時練習的重要性。。最主要的是,這一次是結對編程,兩個人在一起編寫程式,相互提出想法,輕鬆解決小錯誤,有小的問題與失誤常常能夠及時發現,避免了到時候重新觀看代碼耗費大量時間,是一次非常好的體驗。兩人相互督促,效率比較高,雖然沒有一個人打碼那麼自在,但是確實感受到了結對編程的好處。
評價:
我的隊友梁漢烽在我打碼的時候給了我很多的幫助,他常常坐在我旁邊看我打碼,在旁邊給出他的思路,人也很幽默,沒有頭緒的時候也會說一些騷話活躍氣氛。我從他那裡學到了不少東西,他指導我代碼的規範性,讓我對設計程式時的結構性有了進一步的瞭解,而且經常提醒我犯得一些小錯誤。
羅彬進行了對功能的基本架構,並且提供了基本思路,在此過程中我為他提供意見,儘可能的求同存異,改善基本情況,兩個人同時進行編碼可以在編碼過程中看出錯誤,避免BUG的過多產生,導致任務進度的減慢。