前言
(1) 第四次題集的第一題已經經過了三次迭代,需要考慮到的情況越來越複雜,難度也越來越大,這讓我感受到物件導向程式設計的基本原則的重要性,此前每一次迭代都應該謹慎,切忌為了偷懶就破壞類之間的關係(我第二次迭代就偷懶過了所有測試點,然後最後一次就狂改)。
(2) 第五次題集只有三道題,也是第一次迭代難度不大,但是進過前面幾次大作業的磨練,我對類的使用明顯愈發熟練,本次大作業用到了抽象類,我將電路裝置定義為了抽象類,很輕鬆的AC這道題。
然後是這題集的第三題,首次接觸了集合型別,這道題是改錯題,宣告型別是Collection,實際型別是ArrayList,然後使用迭代器遍歷所以員工,難度一般。
(3) 第六次題集只有一道題(看起來挺唬人),難度也沒有很大,經過第一次迭代,加入了並聯電路,電路裝置也變多了,但是有一個關於精度的計算很難考慮到,花了很長時間除錯。
第四次題集
設計與分析:
本次題集除了第三題其他都是很基礎的題目,就分析一下最後一題,以下是類圖:
本次大作業我設計的主要的幾個類分別是
(1) 題目類(Question):用於儲存一道題目的資訊以及處理的方法。(與系列 1 相同,無變化)
private int num;// 題號
private String content, // 題目內容
standardAnswer;// 標準答案
boolean matchingStandardAnswers(String answer):判斷是否符合標準答案
boolean isValid=true;//是否是有效的題目
void disabled() //當題目被刪除,設定題目為失效狀態
ArrayList
(2)試卷題目類(Question_Paper):用於儲存試卷中的題目資訊。由於試卷中的題目序號與題
目本身的題號不一致。且題目在不同試卷中的分值可能不一樣,因此,設計試卷題目類。
int p_q_num;//試卷中題目的順序號
Question question;//題目類的物件,儲存題目資訊
int question_score;// 題目分值
int judge_markAnswer(String answer) // 判斷題目得分
(3)試卷類(TestPaper):儲存和處理一張試卷的資訊以及處理的方法,
int questionNum;// 題目數量
HashMap<String, Question_Paper> questions;//題目清單
void inputQuestion(int num, Question question):新增一道題目
void printQuestions():輸出題目的內容~標準答案
boolean markQuestion(int num, String answer):判斷第 num 題的正確性。
int sum-儲存總分
int questionQuantity-儲存題目數量
int getSum()****:獲得總分
(4)答案類(Answer):用於儲存答卷中一道答案的資訊。
Question_Paper question;
String answer;
boolean mark;// 每一題的正確性
int score=0;// 每一題的得分
void calScore()//計算得分
void disable() //答案對應的題目失效,判分為 0
(5)答卷類(AnswerPaper):儲存和處理一張答卷的資訊以及處理的方法
TestPaper paper;//試卷資訊
String[] answers;//每一題的答案資訊boolean[] marks;//每一題的判題結果(對/錯)
void printQ_A(int num):輸出第 num 題的題目和答案(卷面答案,非標準答案)
boolean getJudge(int num):獲得第 num 題的判題結果
void printJudges() :// 輸出所有的得分以及總分,以空格分隔
String stuNum;// 學號
void** printErrorNumQuestion(Answer answer) // 輸出試卷中錯誤題目號的題目
void** printInvalidQuestion(Answer answer)// 輸出失效題目資訊
(5)學生類(Student):儲存學生的資訊
String stuNum, stuName;
踩坑心得:
(1) 首先這次的大作業,我最後還有一個測試點沒有過,現在我弄明白了,但是時間已經過了,這個題目的空白卷輸入,就是隻有一個卷子,沒有題目序號,應該按正常的卷子輸出,我的正規表示式有問題,沒有捕捉到這種情況。
例如:
輸入:#N:1 #Q:1+1= #A:2
T:1 1-5
X:20201103 Tom
S:1 20201103 #A:
end
應該將這種情況正常輸出。
(2) 我這次又犯了上次同一個錯誤,正規表示式寫錯了,導致捕捉不準確。
public static boolean checkquestion(String str)
{
String regstr="^#N:\\d*\\s*#Q:.*#A:.*";
Pattern pattern= Pattern.compile(regstr);
Matcher matcher=pattern.matcher(str);
if(matcher.find())
{
return true;
}
else
{
return false;
}
}
public static boolean checktest(String str)
{
String regstr1="(\\d*)-(\\d*)";
Pattern pattern1=Pattern.compile(regstr1);
Matcher matcher1=pattern1.matcher(str);
String regstr2="^#T:\\s*\\d+\\s*((\\d+\\s*-\\s*\\d+\\s*)+)$";
Pattern pattern2=Pattern.compile(regstr2);
Matcher matcher2=pattern2.matcher(str);
if(matcher2.find())
{
return true;
}
else
{
return false;
}
}
public static boolean checkstudent(String str)
{
String regstr2="^#X:\\s*\\d+\\s(.*?)+(?:-\\d+\\s(.*?)+)*$";
Pattern pattern1=Pattern.compile(regstr2);
Matcher matcher1=pattern1.matcher(str);
if(matcher1.find())
{
return true;
}
else
{
return false;
}
}
這幾個check方法最後在同學的幫助下終於是改對了,下一次一定要仔細讀取題目意思,謹慎考慮好之後在開始,不然寫到一半出了問題很崩潰的。
改進建議:
這次寫的大作業,還是出現前三次的問題,就是類之間的關係有些模糊不清,寫到後面經常性用錯,但是比起前幾次的亂七八糟的程式碼,至少類的功能分的清楚了。
(1) 這次大作業如果將同學與答卷號碼用HashMap儲存起來,後續只需要透過迭代器就可以遍歷所以答卷和學生,不用再像現在這樣遍歷所以答卷找同學,可以增強程式碼的可讀性,而且程式碼也會更簡潔,維護起來也容易。
(2) 試卷題目類也可以使用List泛型代替ArraysList儲存試卷與題目的關係,這樣可以直接使用Collections.sort方法進行排序。
第五次題集:
設計與分析:
首先是這次題集的第一題,這是第一次出現,還沒有進行迭代,特殊條件比較多難度一般,以下是類圖:
這裡主要設計了兩個類:
(1) 電路裝置類(Equipment):描述所有電路裝置的公共特徵。
abstract class Equipment {
int switchstyle;//如果是開關 開關的狀態
public int getSwitchstyle() {
return switchstyle;
}
public boolean IfPathway()
{
return false;
}
double voltage;//裝置分到的電壓
public double getVoltage() {
return voltage;
}
abstract public void setVoltage(double c);
double resistor;//電阻
abstract double getResistor();
public double getContact1() {
return contact1;
}
public void setContact1(double contact1) {
this.contact1 = contact1;
}
String name;//裝置名
public String getName() {
return name;
}
double contact1;//觸腳1的電壓
public double getContact2() {
return contact2;
}
public void setContact2(double contact2) {
this.contact2 = contact2;
}
double contact2;//觸腳2的電壓
abstract void display();//輸出裝置的狀態
abstract void close();
}
(2) 串聯電路類(Series):一條由多個電路裝置構成的串聯電路,也看成是一個獨立的電路裝置。以下是部分程式碼:
class Series {
public double getVoltage() {
return voltage;
}
public void setVoltage(double voltage) {
this.voltage = voltage;
}
private double voltage;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
public void VoltageDivision()
{
double aver=getVoltage()/getResistor();
for(int i=0;i<list.size();i++)
{
Equipment e= list.get(i);
if(e instanceof Switch || e instanceof Governor || e instanceof ContinueGovernor)
continue;
else
{
e.setVoltage(aver*e.getResistor());
}
}
}
private double resistor;
public double getResistor() {
resistor = 0;
for (int i = 0; i < list.size(); i++) {
Equipment e = list.get(i);
if (e instanceof Switch || e instanceof Governor || e instanceof ContinueGovernor)
continue;
else
resistor += e.getResistor();
}
return resistor;
}
}
將裝置類定義為抽象類,其餘所有電器繼承與裝置類,然後使用串聯電路類,然後將每個電路元件存入其中,
判斷控制電器的狀態,判斷電路是否通路,模擬各種情況,最後輸出各個元件的狀態。
其中串聯電路類內部使用ArrayList型別儲存每一個裝置,最後確定沒一個開關的狀態,最後進行排序輸出即可。
然後是這次題集的第三道題,首次接觸到了集合型別,這道題是一道改錯題,使用Collection
儲存每一個員工,使用迭代器遍歷輸出,難度也一般。
//主函式
public class Main {
public static void main(String[] args) {
// 1、建立有序集合物件
Collection <Employee>c = new ArrayList();
Scanner sc = new Scanner(System.in);
// 建立3個員工元素物件
for (int i = 0; i < 3; i++) {
String employeeName = sc.nextLine();
int employeeAge = sc.nextInt();
sc.nextLine();
Employee employee = new Employee(employeeName, employeeAge);
c.add(employee);
}
// 2、建立迭代器遍歷集合
Iterator <Employee>it=c.iterator();
//3、遍歷
while (it.hasNext()) {
//4、集合中物件未知,向下轉型
Employee e = (Employee) it.next();
System.out.println(e.getName() + "---" + e.getAge());
}
}
這是Main方法部分,宣告瞭一個迭代器it,接收集合c的迭代器,然後遍歷。
踩坑心得:
(1) 這次的大作業難度不大,但是由於看漏了條件,導致最後有幾個測試點一直沒有透過,這道題允許開關多次出現,最後輸出結果要排序。後來我就宣告瞭一個ArrayList型別專門儲存所有開關,只有一遇見開關,就將這開關物件存入,判斷是否有開關存在,如果有就排序輸出。同時開關類也實現了Comparable介面。
@Override
public int compareTo(Switch o) {
if (name.compareTo(o.getName()) < 0)
return -1;
else
return 1;
}
(2) 還有本次大作業的分檔變速器是必須在0-3之間,不能炒出這個範圍的,需要加上特判條件。
例如:
else if(Line.contains("#F"))
{
if(Line.contains("+")&&gears<3)
{
gears++;
}
else if(Line.contains("-")&&gears>0)
{
gears--;
}
}
這樣保證檔位不會超出範圍。
改進建議:
(1) 下一次迭代肯會將入並聯電路,所以應將串聯電路繼承於電器裝置類,這樣方便下次迭代。
(2) 對於其他電路裝置可以像對待開關一樣操作,這次大作業說明了其餘裝置只出現一次,但是下次不一定,所以全部使用List泛型儲存,在實現Comparable介面,方便排序。
以吊扇類為例:
@Override
public int compareTo(Fan o) {
if(name.compareTo(o.getName())<0)
return -1;
else
return 1;
}
if(D.size()!=0)
{
Collections.sort(D);
for(int i=0;i< D.size();i++)
D.get(i).display();
}
這樣所有電路裝置多次出現排序的問題就可以解決。
第六次題集:
設計與分析:
這次大作業比起上次大作業多了一個並聯電路,將並聯電路視為一種特殊的電路元件即可,但同時也需要主要並聯電路的電阻和電壓的計算涉及到精度問他。以下是新的類圖:
這次主要的增加的新類:
(1) 並聯電路類(Parallel):繼承電路裝置類,也看成是一個獨立的類。
以下是主要屬性和方法:
class Parallel extends Equipment {
private ArrayList<Series> seriesList;
Parallel(ArrayList<Series> seriesList) {
this.seriesList = new ArrayList<>();
this.seriesList = seriesList;
}
@Override
public void setVoltage(double c) {
for(int i=0;i<seriesList.size();i++)
{
Series series=seriesList.get(i);
if(series.IfPathway())
{
series.setVoltage(c);
series.VoltageDivision();
}
}
}
@Override
public boolean IfPathway()//檢查並聯是否通路
{
for(int i=0;i<seriesList.size();i++)
{
if(seriesList.get(i).IfPathway())
{
return true;
}
}
return false;
}
@Override
double getResistor() {
for(int i=0;i<seriesList.size();i++)
{
if(seriesList.get(i).Ifshort())
{
return 0;
}
}
double r = 0;
double R = 0;
for (int i = 0; i < seriesList.size(); i++) {
Series s = seriesList.get(i);
if(s.IfPathway())
{
R += (1.0 / s.getResistor());
}
}
r = (1.0 / R);
return r;
}
@Override
void display() {
}
@Override
void close() {
}
}
並聯電路也是一個特殊的電路裝置,所以他也有名字,電阻,電壓。需要注意的是並聯的電阻計算和電壓的計算會有精度問題。
題幹中說明了並聯電路所包含的串聯電路會在其之前輸入,所以只要將每一條路(出了連線電源的串聯)存在一起,建立並聯電路時一起加入就行。
踩坑心得:
(1 )這道題涉及到並聯電路電阻的計算,那麼就肯定會出現一些,無限迴圈小數等非常規小數,導致計算結果出錯。
例如:
輸入:
#T1:[IN K1-1] [K1-2 D2-1] [D2-2 D3-1] [D3-2 OUT]
#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
#M1:[T1 T2]
#T3:[VCC L1-1] [L1-2 M1-IN] [M1-OUT GND]
#K1
#K2
#L1:1.00
end
錯誤輸出:
@K1:closed
@K2:closed
@L1:1.00
@D1:360
@D2:199
@D3:199
這是一個人標準錯誤的答案。
正確輸出:
@K1:closed
@K2:closed
@L1:1.00
@D1:360
@D2:200
@D3:200
當D2的轉速為199.99999時最後應輸出200,而不是199。這是計算電壓是,沒有處理好。需要進行精度判斷,如:
double TF=c1-Math.floor(c1);
double TC=Math.ceil(c1)-c1;
if(Math.min(TF,TC)<1e-10)
{
c1=Math.round(c1);
}
這需要每次計算電壓時加上這個判斷即可AC。
改進建議:
下次迭代是可能會加入並聯電路串聯的情況,所以在加入並聯電路時需要進行遍歷所有此前的串聯,找到符合的串聯,再加入並聯電路中。為了應對各種迭代情況,所以類之間的耦合性一定要設法降低,不能導致牽一髮而動全身,這樣修改程式碼的工作量太大了。
總結:
對物件導向程式設計(OOP)概念理解加深了,Java是一種物件導向的語言,透過這三次作業,更深入地理解類、物件等概念。
然後程式碼組織與結構的能力增強,編寫大型Java程式需要良好的程式碼組織和結構。
逐漸熟練如何使用介面、類組織程式碼,以及如何使用註釋來提高程式碼的可讀性。
除錯能力提高,除錯是開發過程中不可或缺的一部分。學會如何使用除錯工具和技巧來定位和修復程式碼中的錯誤。
還精通了許多類的使用例如LinkList,ArraysList,HashMap等等。
然後是一些建議:
對於模擬家用電器控制系統可以在並聯電路中加入一些特殊的元件,如電阻器,變壓器等等,這樣更符合實際情況。
之後的PTA作用可以加入一些演算法資料結構的知識,促進我們自學,這幾次作用都沒有什麼演算法知識。
可以加入更多關聯實際的題目,增強對物件導向的理解。