代碼地址https://coding.net/u/huhulong/p/sizeyunsuan/git/tree/master/
(一)需求分析

其中,模型類有Number類,實例成員變量有整數部分,分子,分母
這三個組成一個帶分數,並且用構造方法
實現自動生成不小於自然數upperLimit的帶分數。
然后是符號類,這個類比較簡單,只有一個實例變量,該實例變量代表着四則運算的四種符號,並且在類里頭定義了隨機生成四種運算符的構造方法
接着是式子類Formula,我給他定義了兩個List類型的實例變量,一個numberList存放一個式子的所有隨機生成帶分數,另一個operatorList存放式子中所有的算符,
並且定義了隨機生成若干個帶分數和算符組合起來的構造方法,將生成的帶分數和算符分別存入到實例變量中,
我在該類中定義了兩個實例方法,
第一個是將數列表和符號列表組合成式子的方法
第二個是計算該式子結果的方法
最后一個類是主類Test類,我將對文件的操作都放在主類中進行。
然后是四個文件夾,
Exercises.txt:生成題目文件夾
Answers.txt:生成答案文件夾
Grade.txt:成績展示文件夾
YourFile.txt:使用者將答案寫在該文件夾中
(四)代碼說明
1.生成隨機數算法
本身該算法並不難,但是要排除三種情況,第一種是帶分數的整數部分為0的情況,就只展示分數部分,第二種是分數部分的分子和分母相同,這時候整數部分+1,並且不展示
分數部分,第三種情況是分子為0,此時分數部分不展示,只展示整數部分。
2.生成四則算式並將其轉換為字符串的方法
由於算式都是“算符數量=帶分數數量-1”,所以我們明白在列表中一個算符的前一個數和后一個數相對於該算符在列表中的下標是x和x+1(x代表某個算符在列表中的位置)。
3.算式答案生成算法
思路是這樣:
由於乘算和除算的優先級高於加減,所以需要先對按順序對式子的乘算和除算先進行計算后才能對式子的加減算進行計算
舉個例子,式子的實例變量兩個列表,一個存放數字,一個存放算符
第一次計算時
numberList: 5 ,10 , 15 , 20;
operatorList: +. * , ÷
先進行乘除檢測,從operatorList中檢測,檢測到列表下標1為“*”,此時,取到numberList中的下標為1和2的數,並把他們相乘,得到150的結果,並且把他們放入numberList的10前面,然后去掉10,15兩個數,同樣的,把operatorList中的“*”符remove掉
第二次計算時
NumberList:5 , 150 , 20;
operatorList:+,÷
乘除檢測還在繼續,此時檢測到÷符,根據第一次方法的方式,此時剩下一個算符
第三次計算
numberList:5 ,7’1/2;
operatorList: +
乘除算法的結束情況為當operator列表中不存在乘除符號,此時進行加減算法,並得到最后結果
numberList:12’1/2;
operator:空
此時得到算式結果為12’1/2;
使用這樣的思路,無論算符有多少個,都可以用該方法算出結果。
//生成式子答案
public Number getAnswser() {
//先對式子的*和÷進行處理
int i = 0;
while (i < operatorList.size()) {
//如果數列里的數個數是0,則返回該方法
if (numberList.size()==1){
return (Number) numberList.get(0);
}
//當算符出現*和÷的時候
if (operatorList.get(i).equals("*") || operatorList.get(i).equals("÷")) {
//取得運算符兩側的數
Number numberFront = (Number) numberList.get(i);
Number numberAfter = (Number) numberList.get(i + 1);
//求得運算符兩側數值的分子部分
int numberFrontMolecule = numberFront.getInteger() * numberFront.getDenominator() + numberFront.getMolecule();
int numberAfterMolecule = numberAfter.getInteger() * numberAfter.getDenominator() + numberAfter.getMolecule();
int endMolecule = 0;
int endDenominator = 0;
//算得兩數相乘的分子值和分母值
if (operatorList.get(i).equals("*")) {
endMolecule = numberFrontMolecule * numberAfterMolecule;
endDenominator = numberFront.getDenominator() * numberAfter.getDenominator();
}
if (operatorList.get(i).equals("÷")) {
endMolecule = numberFrontMolecule * numberAfter.getDenominator();
endDenominator = numberAfterMolecule * numberFront.getDenominator();
}
int endInteger = 0;//初始分子的整數部分為0
if (endMolecule > endDenominator) {
endInteger = endMolecule / endDenominator;
endMolecule = endMolecule % endDenominator;
}
//求最大公約數
int smaller = endMolecule > endDenominator ? endMolecule : endDenominator;
int maxCommonFactor = 1;
for (int j = 1; j <= smaller; j++) {
if (endMolecule % j == 0 && endDenominator % j == 0) {
maxCommonFactor = j;
}
}
endMolecule = endMolecule / maxCommonFactor;
endDenominator = endDenominator / maxCommonFactor;
Number endNumber = new Number(endInteger, endMolecule, endDenominator);//約分后的式子
this.numberList.add(i, endNumber);
this.numberList.remove(i + 1);
this.numberList.remove(i + 1);
this.operatorList.remove(i);
}else {
i++;
}
}
//進行完*÷處理后進行+-處理
while (operatorList.size()!=0){
//取得運算符兩側的數
Number numberFront = (Number) numberList.get(0);
Number numberAfter = (Number) numberList.get(1);
//求得運算符兩側數值的分子部分
int numberFrontMolecule = numberFront.getInteger() * numberFront.getDenominator() + numberFront.getMolecule();
int numberAfterMolecule = numberAfter.getInteger() * numberAfter.getDenominator() + numberAfter.getMolecule();
//不管兩式子的分母一樣或不一樣,都把兩分母相乘
int endDenominator=numberFront.getDenominator()*numberAfter.getDenominator();
//如果為+符,就進行+算
int allMolecule1=numberFrontMolecule*numberAfter.getDenominator();
int allMolecule2=numberAfterMolecule*numberFront.getDenominator();
int endMolecule=0;//定義未約分前的分子部分
if (operatorList.get(0).equals("+")){
endMolecule=allMolecule1+allMolecule2;
}
if (operatorList.get(0).equals("-")){
endMolecule=allMolecule1-allMolecule2;
}
int endInteger = 0;//初始分子的整數部分為0
if (endMolecule > endDenominator) {
endInteger = endMolecule / endDenominator;
endMolecule = endMolecule % endDenominator;
}
//求最大公約數
int smaller = endMolecule > endDenominator ? endMolecule : endDenominator;
int maxCommonFactor = 1;
for (int j = 1; j <= smaller; j++) {
if (endMolecule % j == 0 && endDenominator % j == 0) {
maxCommonFactor = j;
}
}
endMolecule = endMolecule / maxCommonFactor;
endDenominator = endDenominator / maxCommonFactor;
Number endNumber = new Number(endInteger, endMolecule, endDenominator);//約分后的式子
this.numberList.add(0, endNumber);
this.numberList.remove(1);
this.numberList.remove(1);
this.operatorList.remove(0);
}
return (Number) numberList.get(0);
}
(五)測試運行
1.生成題目
Test主函數: Exercises.txt文件夾: Answers.txt文件夾
2.檢測題目對錯
YourFile.txt文件夾 主函數Test Grade.txt文件夾
(六)PSP展示
(七)小結
其實如果打代碼時思路清晰的話,編碼的時間應該不會很長,但是有些細節可能會導致程序出bug,后面調bug的時間也用了非常長的時間。
然后說說此次作業遇到的一些坑,一個是文件的寫入操作,如果沒有使用flush,寫入操作是不會進行的,感覺當初學習java時沒有太深入得去理解,總是覺得flush和close綁定起來用,沒有去注意它主要的作用是什么,
然后是對列表的操作,由於在計算結果的算法中,需要對列表中的值進行添加和刪除,會造成列表的下標和長度發生變動,一不注意就容易出現數組越界的異常。
然后就是對重復性檢測的一些思路,
1.讀取題目文件,並且用一個列表來存放讀取到的字符串
2.對該列表進行操作,取出每個列表中的成員,並且進行如下操作:
(1)用字符串的CharAt方法,一步步讀至算符,算符前面的部分作為分數/帶分數/整數部分存入List列表中,將算符存入到另一個operatorList列表中
(2)將讀取的數值部分根據讀到的 ’ 和 /符號,將數值部分分成整數部分和分子,分母部分,存入到numberList列表中
(3)重復(1)(2)部分,即可將文件中讀到的式子全部轉換成一個個的算式對象(算式對象中有operatorList和numberList兩個實例變量)
(4)根據÷和-號兩邊不可變,*和+號兩邊可以變動來判斷兩個式子的重復性。