結對編程項目—四則運算 第二周 整體總結
阿新 • • 發佈:2019-04-15
set 完成 floating har calc || 形式 fault standard
20175214林郅聰 結對編程項目—四則運算 第二周 整體總結
一、需求分析
1.題目要求:
實現一個命令行程序,要求:
自動生成小學四則運算題目(加、減、乘、除)
- 支持整數
- 支持多運算符(比如生成包含100個運算符的題目)
- 支持真分數
統計正確率
2.擴展需求
- 可以實現多語言,包括簡體中文,繁體中文和英文
- 生成題目去重
- 輸入題目到文件,從文件中讀取題目並判斷對錯
3.深度需求
本次結對項目要求完成小學生的四則運算,除了題目中要求的內容外,還可以包含以下方面
- 進行難度更高的運算,比如嵌套運算或其他運算符例如求方冪、求整、取余等
- 由於是通過程序完成,在給出測試的正確率的情況下可以判斷正確率是否達到合格標準,若未達到則繼續進行直到達到標準為止
- 讓學生自由選擇題目難度,根據不同難度給出相應評價
二、設計思路
在開始本周任務前,我對我們的程序進行了簡要的分析,和上周相比有如下的改變
- 增加了真分數的運算,並控制分數的格式(在後面會有說明)
- 給出了多語言的支持:簡體中文、繁體中文、英文
- 生成了題目去重的功能:在原來的基礎上進行了適當的修改,因為當生成算式較短時比較容易重復,但這一部分的代碼還存在一些問題,目前只能判斷兩道題目是否完全相同,不能排除
1 + 2
和2 + 1
這種重復的情況
UML類圖
三、變動的代碼
- 首先是分數的形式,選擇將分號“/”也當做隨即字符和加減乘除一起隨機產生,而在中綴轉後綴運算式將分號的優先級設置為僅低於有括號,這樣可以區別分號“/”和除號“÷”
public int compareValue(char chi) { int number = 0; switch (chi) { case '(': number = 1; break; case '+': case '-': number = 2; break; case '*': case '÷': number = 3; break; case '/': number = 4; break; case ')': number = 5; break; default: number = 0; break; } return number; }
- 如何產生真分數?分數的產生很容易,那如何控制產生真分數呢?和其他小組的同學討論研究後發現他們的方式都不適合我們,因為在產生算式用到的方法不同,分數的生成方式也存在差異,因此他們聲稱分數的方法並不適合我們。在和結對夥伴討論後,我們決定先產生分子,根據分子的範圍控制分母的大小
else if (rand == 0 && flag2 % 2 == 0 || flag5 % 3 == 0 && flag2 % 2 == 0 { //插入數字
if(flag6==1) //當產生了分號時
n = num.GetNumber(a-n)+n; //控制分號後面的數比分號前面的數大但不會超過給出的最大值
else n = num.GetNumber(a);
q += n;
flag2++;
flag5 = 0;
countTotal++;
- 接下來是真分數的產生和運算部分,關於這一部分,在編寫之前花了很長時間思考,因為分數在運算時涉及到了壓棧的問題,而在運算時如果將分子分母和分號分開壓棧那麽就失去了本來的意義,如果將分數控制為字符串的形式進行壓棧操作,那麽之前的代碼需要全部重寫,最後在翻看教材的時候發現的書上第四章Example4_23中給出了一個有理數Rational類,可以通過該類進行分數的運算,而這種方法也正適合我們之前寫的程序。在對書上的例子進行少許修改後,應用到我們的程序中
Rational div(Rational r) { //除法運算
int a=r.getNumerator();
int b=r.getDenominator();
int newNumerator=numerator*b;
int newDenominator=denominator*a;
Rational result=new Rational();
if(a==0) {
System.out.println("該算式無解");
result.setNumerator(0);
}
else {
result.setNumerator(newNumerator);
result.setDenominator(newDenominator);
}
return result;
}
簡要說明:這一部分的程序與書上基本相同,但由於書上是控制輸入,因此不需要排除分母為0的情況,而我們的程序中數字時隨機產生的,可能會在運算的過程中產生了計算出的結果是0作為了分母,因此需要控制這種情況
多語言選擇:在完成這一部分內容時,我們選擇了用抽象類的方法,將每一種提示作為一個抽象方法,三種語言作為子類重寫其中的方法
package CalculateSystem;
public abstract class Language {
public abstract String getQuestionNumber();
public abstract String getMaxNumber();
public abstract String getMaxOperator();
public abstract String getInputAnswer();
public abstract String getRight();
public abstract String getWrong();
public abstract String getRate();
public abstract String getContinue();
public abstract String getThanks();
}
下面是子類English中重寫的方法
public class English extends Language {
public String getQuestionNumber() {
return "Enter the number of questions you want to do:";
}
public String getMaxNumber() {
return "Please enter the maximum number you want to operate:";
}
public String getMaxOperator() {
return "Please enter the length of the expression you want to operate:";
}
public String getInputAnswer() {
return "Enter your answer :(floating point)";
}
public String getRight() {
return "Congratulations! Your answer is right";
}
public String getWrong() {
return "Wrong answer! The correct answer is";
}
public String getRate() {
return "Test over! Your accuracy is";
}
public String getContinue() {
return "Do you want to continue? Please enter 1 to continue and 0 to exit";
}
public String getThanks() {
return "Test over, thanks for using!";
}
}
- 題目去重:這一部分的代碼的功能 並不全面,只限於判斷兩道題目是否完全相同,並不能判斷其中是否含有沒有意義的重復
GetQuestion q = new GetQuestion();
String question1 = q.get(maxnum,maxop);
if(question1.equals(question2)) { //判斷時候和前一題是否相等,相等則不計入次數,重新產生等式
i--; continue;
}
System.out.println(question1);
question2 = question1; //不相同則把該題目賦值給question2用於和下一題進行比較
四、遇到的問題
- 開始時我和夥伴在考慮如何生成真分數的同時,也在考慮如何保證過程中也都控制真分數,後來發現沒有必要糾結這個問題,只需要控制在生成時生成簡單的真分數,例如
13/15
即可,因為在運算過程中給存在括號優先級的問題,例如(15*3+8)/16
並不需要在運算過程中控制都是真分數 - 在進行壓棧和彈棧運算時,將彈出的數轉化為有理數時,發現第一個彈出的數總是會被第二個數覆蓋,在探討測試後發現是因為定義了一個變量num,他的作用是,當棧中的數不是運算符的時候,要將其放回。這個變量定義在了
if-else
語句外,因此只有最開始初始化了變量,在運算過程中一直使用後面的數覆蓋前面的數,因此彈出的兩個數會一樣,更該方法是將變量的初始化放在else語句中,每次判斷時,都要初始化,這樣不會對之前的產生影響。
while (token.hasMoreTokens()) {
temp = token.nextToken();
if (Isop(temp) == 1)//遇到操作符,彈出棧頂的兩個數進行運算 {
op2 = (Rational) stack.pop();
op1 = (Rational) stack.pop();//彈出最上面兩個操作數
result = cal(temp.charAt(0), op1, op2);//根據運算符進行運算
stack.push(result);//將計算結果壓棧
}
else {
Rational num = new Rational(); //每次重新初始化
num.setNumerator(Integer.parseInt(temp));
stack.push(num);//操作數入棧
}
}
- 產生分數時會產生
5/7/8
的形式,事實上這種分數產生的意義不大,我們希望避免這種情況,幾經嘗試後選擇在產生問題的類中再加flag變量進行控制
五、運行過程截圖
六、代碼托管
https://gitee.com/fzlzc/java2019/tree/master/src/CalculateSystem
七、對結對夥伴的評價
我覺得我的小夥伴非常棒,這周他主要負責添加真分數功能和語言包,這部分基本由他獨立完成,我只負責對接,最後拼接雖然有些磕磕絆絆,有許多沒有發現的bug錯誤,但是一一糾正檢驗,一起討論學習的氛圍讓我覺得十分酣暢,而且成果還是比較讓我們滿意的,基本完成了預期,希望以後可以繼續這樣快樂合作,快樂編程。
### 八、預估時間與實際時間
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
?Estimate | ? 估計這個任務需要多少時間 | 570 | 815 |
Development | 開發 | ||
? Analysis | ? 需求分析 (包括學習新技術) | 30 | 40 |
? Design Spec | ? 生成設計文檔 | 30 | 30 |
? Design Review | ? 設計復審 (和同事審核設計文檔) | 20 | 40 |
? Coding Standard | ? 代碼規範 (為目前的開發制定合適的規範) | 20 | 60 |
? Design | ? 具體設計 | 20 | 25 |
? Coding | ? 具體編碼 | 360 | 500 |
? Code Review | ? 代碼復審 | 20 | 20 |
? Test | ? 測試(自我測試,修改代碼,提交修改) | 30 | 50 |
Reporting | 報告 | ||
? Test Report | ? 測試報告 | 20 | 20 |
? Size Measurement | ? 計算工作量 | 10 | 10 |
? Postmortem & Process Improvement Plan | ? 事後總結, 並提出過程改進計劃 | 10 | 20 |
合計 | 570 | 815 |
結對編程項目—四則運算 第二周 整體總結