碼雲位址:https://git.oschina.net/lzx84/szys
題目:
從《建構之法》第一章的 “程式” 例子出發,像阿超那樣,花二十分鐘寫一個能自動生成國小四則運算題目的指令行 “軟體”。
滿足以下需求:
- 除了整數以外,還要支援真分數的四則運算,真分數的運算,例如:1/6 + 1/8 = 7/24
- 運算符為 +, −, ×, ÷
- 并且要求能處理使用者的輸入,并判斷對錯,打分統計正确率。
- 要求能處理使用者輸入的真分數, 如 1/2, 5/12 等
-
使用 -n 參數控制生成題目的個數,例如執行下面指令将生成10個題目
Myapp.exe -n 10
需求分析:
- 需要處理分數,包括化簡分數、識别整數等。并考慮特殊情況,如分數分母不為0等。考慮到題目要求國小水準,則分子分母數值不超過100。
- 需要編寫分數四則運算的算法,并考慮特殊情況,如減法結果不能為負數,除法中除數不能為0等。
- 需統計使用者正确的題目數,與總題目數。
- 需要實作字元串的識别與比較。
功能實作:
- 基本功能:能随機生成若幹四則運算式,并檢測使用者答案,統計正确率。
- 擴充功能:實作多項運算、統計做題時長、避免重複題型、控制題目難度等(未實作)
- 進階功能:實作錯題統計功能、實作多使用者競賽功能、實作更複雜的運算等(未實作)
設計實作:
Fraction類:
Calculate類:
Dofrac類:
代碼說明:
Fraction類:
public class Fraction {
private int fenzi;
private int fenmu;
public Fraction() {
super();
}
public Fraction(int fenzi, int fenmu) {
super();
this.fenzi = fenzi;
this.fenmu = fenmu;
this.simplify(fenzi, fenmu); //在構造函數中直接化簡分數(感覺此方法欠妥)
}
public void simplify(int fenzi, int fenmu) {
int GCD = Dofrac.GCD(fenzi, fenmu);
this.fenzi = fenzi / GCD;
this.fenmu = fenmu / GCD;
}//化簡分數
@Override
public String toString() {
if (fenzi == 0) {
return 0 + "";
} else if (fenzi % fenmu == 0) {
return fenzi / fenmu + "";
} else
return fenzi + "/" + fenmu;
}//改寫toString,輸出為分數形式
public int getFenzi() {
return fenzi;
}
public void setFenzi(int fenzi) {
this.fenzi = fenzi;
}
public int getFenmu() {
return fenmu;
}
public void setFenmu(int fenmu) {
this.fenmu = fenmu;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + fenmu;
result = prime * result + fenzi;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Fraction other = (Fraction) obj;
if (fenmu != other.fenmu)
return false;
if (fenzi != other.fenzi)
return false;
return true;
}
}
Calculate類:
public class Calculate {
public static Fraction add(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fm2 + fm1 * fz2, fm1 * fm2);
return f;
}//加法運算
public static Fraction sub(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fm2 - fm1 * fz2, fm1 * fm2);
return f;
}//減法運算
public static Fraction mul(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fz2, fm1 * fm2);
return f;
}//乘法運算
public static Fraction div(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
Fraction f = new Fraction(fz1 * fm2, fm1 * fz2);
return f;
}//除法運算
public static boolean compare(Fraction f1, Fraction f2) {
int fz1 = f1.getFenzi();
int fz2 = f2.getFenzi();
int fm1 = f1.getFenmu();
int fm2 = f2.getFenmu();
if (fz1 * fm2 >= fz2 * fm1) {
return true;
} else{
return false;
}
}//比較兩分數的大小
}
Dofrac類:
public class Dofrac {
public static Fraction CreatFrac() {
int fz, fm,co;
co=(int)(Math.random()*2);//co取[0,1]得随機值,分别代表分數和整數
if(co==0){
fm = (int) (2 + Math.random() * (100 - 2 + 1));//分母為2-100的随機數
fz =(int) (0 + Math.random() * (fm - 0 + 1));//分子為 0--fm 的随機數(確定真分數)
}else {
fm=1;
fz=(int) (0 + Math.random() * (100 - 0 + 1));
}
Fraction frac = new Fraction(fz, fm);
return frac;
}//建立随機分數
public static boolean check(String input) {
if (input.matches("[0-9]+")||input.matches("[0-9]+/[0-9]+")) {
return true;
} else
{
return false;
}
}//利用正規表達式處理使用者輸入的答案
public static int GCD(int m, int n) {
while (true) {
if ((m = m % n) == 0)
return n;
if ((n = n % m) == 0)
return m;
}
}//計算最大公約數
public static Fraction MakeFor(Fraction f1, Fraction f2, int op, int i) {
Fraction result = new Fraction();
switch (op) {
case 1: {
result = Calculate.add(f1, f2);
System.out.println("第" + i + "題:" + f1.toString() + "+" + f2.toString());
break;
}
case 2: {
if (!Calculate.compare(f1, f2)) {
Fraction temp = f1;
f1 = f2;
f2 = temp;
}//比較兩分數的大小,若減數小于被減數,則将兩個數交換
result = Calculate.sub(f1, f2);
System.out.println("第" + i + "題:" + f1.toString() + "-" + f2.toString());
break;
}
case 3: {
result = Calculate.mul(f1, f2);
System.out.println("第" + i + "題:" + f1.toString() + "*" + f2.toString());
break;
}
case 4: {
if (f2.getFenzi() == 0) {
f2.setFenzi((int) (1 + Math.random() * (10 - 1 + 1)));
}//若除數分子為0,則分子重新取一個1——100的随機數
result = Calculate.div(f1, f2);
System.out.println("第" + i + "題:" + f1.toString() + "÷" + f2.toString());
break;
}
}
return result;
}
}
Generator類(執行類)
public class Generator {
public static void main(String[] args) {
try {
int op = 0;
String input;
int flag = 0;//用于檢測使用者答對的題目數
Scanner sc = new Scanner(System.in);
System.out.println("【四則運算題目生成器】");
System.out.println("請輸入需要的題目數量:");
int n = sc.nextInt();
for (int i = 1; i <= n; i++) {
Fraction f1 = Dofrac.CreatFrac();
Fraction f2 = Dofrac.CreatFrac();
op = (int) (Math.random() * 4 + 1);
Fraction result = Dofrac.MakeFor(f1, f2, op, i);
input = sc.next();
while (!Dofrac.check(input)) {
System.out.println("輸入有誤,請重新輸入:");
input = sc.next();
}
if (input.equals(result.toString())) {
System.out.println("回答正确");
System.out.println("---------------------------------------------");
flag++;
} else {
System.out.println("回答錯誤,正确答案是:" + result.toString());
System.out.println("---------------------------------------------");
}
}
System.out.println("---------------------------------------------");
System.out.println("答題完畢,你的正确率為" + 100 * flag / n + "%");
sc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
測試運作:
PSP表:
PSP2.1 | Personal Software Process Stages | Time (%) Senior Student | Time (%) |
Planning | 計劃 | 5min | 30min |
· Estimate | 估計這個任務需要多少時間 | 8min | 10min |
Development | 開發 | 2h | 1h30min |
· Analysis | 需求分析 (包括學習新技術) | ||
· Design Spec | 生成設計文檔 | ||
· Design Review | 設計複審 | 3min | 1min |
· Coding Standard | 代碼規範 | ||
· Design | 具體設計 | 1h | |
· Coding | 具體編碼 | 40min | |
· Code Review | 代碼複審 | ||
· Test | 測試(自我測試,修改代碼,送出修改) | ||
Reporting | 報告 | ||
· | 測試報告 | ||
計算工作量 | |||
并提出過程改進計劃 |
總結:
此次作業整體難度不大,但是剛開始看到題目構思的時候,卻一度陷入僵局。也許是因為太久沒有程式設計,很多知識都忘記了,導緻剛開始的時候不懂從何下手。在思考了很久,複習了很多以前的知識後,大體上有了思路,但也不是很清晰,想到什麼寫什麼,迷迷糊糊就完成了。中間遇到的BUG也不是很多,但是調試花了很多時間,感覺這是自己程式設計思想不夠系統,對軟體的應用也不是很熟悉造成的,根本原因還是在于練習不夠,經驗不足。希望以後多加練習,可以更加進步。最後感謝老師們和助教的辛勤付出,你們辛苦了!