PTA4-6次作業

代码-爽發表於2024-06-09

1.前言:
第四次作業難度較前三次更大,新增有多選題、填空題,不僅要考慮錯選(直接錯)、少選(部分錯)和多選(直接錯),還需考慮順序問題(BCD與CBD要求結果相同)這給判斷是否正確的難度增加了一個檔次,同時還出現了多試卷多人答題的情形,雖然刪除了第三次作業中刪除題目的部分,但整體難度仍舊較高,若前幾次作業效果不理想,這次作業更加難以動手。第五次作業是新的題目,與前四次無關,其要求寫家居強電電路模擬程式,該次作業限定條件多,難度小,但也需為後面的作業打好基礎,該次作業只要求一整個串聯,但要把開關、分檔調速器、連續調速器、白熾燈、日光燈、吊扇類先建立好,並確定好繼承關係,串聯類和並聯類暫時不用可搭好框架以備不時之需。第六次作業是在第五次作業基礎上新增並聯物件和串聯物件,還新增了落地扇類,總體難度上升。
2.設計與分析:
第四次作業:
修改輸入提取內容後,修改答案類與答卷類,在主函式中修改答案判斷條件,新增Kquestion與Zquestion類區分多選和填空題,無需多加方法,繼承構造就行,主要用於區分
Kquestion與Zquestion類如下:

點選檢視程式碼
public class Kquestion extends Question {
	public Kquestion(int num,String content,String standard_answer){
        super(num,content,standard_answer);
    }
}
public class Zquestion extends Question {
	public Zquestion(int num,String content,String standard_answer){
        super(num,content,standard_answer);
    }
}


內容乏善可陳,在主函式中修改部分如下:

點選檢視程式碼
        int flag=1;
        for(AnswerPaper r : listAnswerPaper)
        {
            flag=1;
            for (Paper p:listPaper) 
            {
                if(r.getNum()==p.getNum())
                {
                    flag=0;
                    for(int i = 0; i < p.getList().size()&& i < r.getList().size(); i++)
                    {
                        Question w=p.getList().get(i);
                        Answer x=r.getList().get(i);
                        if(w.getNum()==num_of_cut)
                        {
                            x.setGrade(0);
                            System.out.println("the question "+w.getNum()+" invalid~0");
                        }
                        else
                        {
                            if(p.getHaveque())
                            {
                                System.out.print(w.getContent()+"~"+x.getAnswer()+"~");
                                if(w.getStandard_Answer().equals(x.getAnswer()))
                                {
                                    System.out.println("true");
                                    x.setGrade(p.getScore(i));
                                    r.setSum(p.getScore(i));
                                }
                                else if(w.getStandard_Answer().contains(x.getAnswer()))
                                {
                                    System.out.println("partially correct");
                                    x.setGrade((int) p.getScore(i)/2);
                                    r.setSum((int) p.getScore(i)/2);
                                }
                                else
                                {
                                    System.out.println("false");
                                    x.setGrade(0);
                                }
                            }
                            else
                            {
                                System.out.println("non-existent question~0");
                                x.setGrade(0);
                            }
                        }
                    }
                    for(int i = 0; i < p.getList().size() - r.getList().size(); i++)
                    {
                        System.out.println("answer is null");
                    }
                    int flag1=0;
                    for(int j = 0;j<listStudent.size();j++){
                        
                        if(r.getNums().equals(listStudent.get(j).getNum()))
                        {
                            System.out.print(r.getNums()+" "+listStudent.get(j).getName()+": ");
                            for(int i = 0; i < p.getList().size(); i++)
                            {
                                if(i < r.getList().size())
                                {
                                    Answer x=r.getList().get(i);
                                    System.out.print(x.getGrade());
                                }
                                else
                                    System.out.print("0");
                                if(i!=p.getList().size()-1)
                                    System.out.print(" ");
                                else
                                    System.out.print("~");
                            }
                            System.out.println(r.getSum());
                            flag1=1;
                            break;
                        }
                    }
                    if(flag1==0)
                    {
                        System.out.println(r.getNums()+" not found");
                    }
                }
            }
        }
        if(flag==1)
            System.out.println("The test paper number does not exist");
我用contains試圖直接識別答案,但遇到多選無能為力,我又試圖用標準答案做contains,但AB可行,AC出問題,直到結束也未能想出簡便方法,其他類與第三次作業基本相同,類圖如下:

SourceMontor的生成報表如下:

由此可見程式碼任存在許多的不足(方法的功能較為單一、最複雜方法行的數量過高有待最佳化、程式碼中的塊結構相對簡單、註釋少等),第四次作業在第三次作業的基礎上增加了多選題和填空題,導致答案判斷頻頻出錯,另外,作業中還增加了多試卷,多學生,使得順序輸出失敗,且亂序輸入也需新增條件使其順序。這次作業我做出了一部分改進,開始有意識的遵循或運用單一職責原則、開閉原則、迪米特法則。因改動小,時間分配問題,得分很低。
第五次作業:
設定總的父類Equipment,讓裝置分為受控與控制分別繼承,新增各子類,讀取輸入並儲存個部分電器,根據要求進行狀態調整,最後重置輸入、輸出電壓,調整引數,再按要求順序輸出即可。因為只有一條幹路,無需考慮太多情況,只需優先判斷開關狀態,且新增開關開啟時整個電路斷路最優先即可。
主要部分在主函式中其它用電器、控制器大體相同,只展示其一。
Fan(用電器):

點選檢視程式碼
private double speed;
    public Fan(double inputV,String name){
        super(inputV,name);
        this.speed=0;
    }
    public void setDeviation(double inputV,double outputV){
        this.deviation=inputV-outputV;
    }
    public void setSpeed(){
        if(this.deviation<80){
            this.speed=0;
        }
        else if(this.deviation==80){
            this.speed=80;
        }
        else if(this.deviation>80&&this.deviation<=150){
            this.speed=80+4.00*(deviation-80);
        }
        else if(this.deviation>150){
            this.speed=360;
        }
    }
Switch(控制器):
點選檢視程式碼
protected int state;
    public Switch(double inputV,String name){
        super(inputV,name);
        this.state=0;
    }
    public void setState(){
        if(this.state==0) {
        	this.state = 1;
        }   
        else{
        	this.state = 0;
        }
    }
    public void setOutputV(){
        if(this.state==0){
            this.outputV=0;
        }
        else if(this.state==1){
            this.outputV=this.inputV;
        }
    }
SourceMontor的生成報表如下:![](https://img2024.cnblogs.com/blog/3428410/202406/3428410-20240609132514588-570698767.png)

類圖如下:

第六次作業:
第六次作業在第五次作業的基礎上增加了串聯和並聯物件,關鍵點在於將每個電器指向同一個物件再改變狀態,不考慮亂序輸入的情況使得並聯好寫很多,另外新增了電阻,同時電流亦不可少,新增落地扇類,實際可歸於受控類的子類,但由於物件數量增多,數字不再具有唯一性,使得儲存查詢對應難度上升,我就因為指向的物件錯誤導致修改狀態時未修改到正確的物件至錯。
還有註釋問題,時間線長到可以讓人模糊類中方法的作用,新增註釋才可以更好的修改,此次作業使用了許多類,但是並沒有遵守或運用單一職責原則、開閉原則、迪米特法則,且許多判定過程寫在了主方法中,而不是寫在類中作為類的方法,更沒有代理類。因為在敲之前,很多邏輯部分並沒有思考清楚,導致程式碼出現了許多的冗餘及不必要的程式碼,我意識到了寫註釋的重要性。寫註釋不僅有益於在寫程式碼過程中清晰自己的邏輯,更有益於改程式碼過程中讀懂自己之前寫的程式碼。在主函式中的輸出和修改狀態部分,未找到簡便快捷的方法,過多的全迴圈使得程式碼過於浪費空間且耗時,無效迴圈非常多。因介面與抽象類不熟練,未使用。
在串聯類和並聯類中都新增了電器類陣列,將主函式中的提取過程放在了其中,簡化了主函式。

點選檢視程式碼
if (matcher1.find()) {
				String name = matcher1.group(1);
				String a = matcher1.group(2);
				String b = matcher1.group(3);
				String c = matcher1.group(4);
				Equipment test = new Concatenation(0, name);
				test.setEq(b,hmCon,hmPa);
				hmCon.put(name, test);
			} else if (matcher2.find()) {
				String name = matcher2.group(1);
				String a = matcher2.group(2);
				Equipment test = new Parallel(0, name);
				test.setCon(a,hmCon);
				hmPa.put(name, test);
程式碼如下: 串聯類:
點選檢視程式碼
public void setEq(String str,HashMap<String,Equipment> hm1,HashMap<String,Equipment> hm2) {
		String test = str.replaceAll("\\[", "").replaceAll("\\]", "");
		String str1[] = str.split(" ");
		for (int i = 0; i < str1.length; i++) {
			Pattern pattern = Pattern.compile("([A-Z])([0-9])-[12]");
			Matcher matcher = pattern.matcher(str1[i]);
			Pattern pattern1 = Pattern.compile("([A-Z])([0-9])-[A-Z]+");
			Matcher matcher1 = pattern1.matcher(str1[i]);
			if (matcher.find()) {
				String name = matcher.group(1) + matcher.group(2);
				String a = matcher.group(1);
				if (i == 0) {
					if (a.equals("K")) {
						Equipment sw = new Switch(this.inputV, name);
						eq.add(sw);
					} else if (a.equals("F")) {
						Equipment bi = new Binning(this.inputV, name);
						eq.add(bi);
					} else if (a.equals("L")) {
						Equipment co = new Coiled(this.inputV, name);
						eq.add(co);
					} else if (a.equals("B")) {
						Equipment la = new Lamp(this.inputV, name);
						eq.add(la);
					} else if (a.equals("R")) {
						Equipment fl = new Fluorescent(this.inputV, name);
						eq.add(fl);
					} else if (a.equals("D")) {
						Equipment fa = new Fan(this.inputV, name);
						eq.add(fa);
					} else if (a.equals("A")) {
						Equipment floor = new Floor(this.inputV, name);
						eq.add(floor);
					}
				} else if (i !=0 ) {
					if(!name.equals(this.eq.get(eq.size()-1).getname())){
					if (a.equals("K")) {
						Equipment sw = new Switch(eq.get(eq.size()-1).getOutputV(), name);
						eq.add(sw);
					} else if (a.equals("F")) {
						Equipment bi = new Binning(eq.get(eq.size()-1).getOutputV(), name);
						eq.add(bi);
					} else if (a.equals("L")) {
						Equipment co = new Coiled(eq.get(eq.size()-1).getOutputV(), name);
						eq.add(co);
					} else if (a.equals("B")) {
						Equipment la = new Lamp(eq.get(eq.size()-1).getOutputV(), name);
						eq.add(la);
					} else if (a.equals("R")) {
						Equipment fl = new Fluorescent(eq.get(eq.size()-1).getOutputV(), name);
						eq.add(fl);
					} else if (a.equals("D")) {
						Equipment fa = new Fan(eq.get(eq.size()-1).getOutputV(), name);
						eq.add(fa);
					} else if (a.equals("A")) {
						Equipment floor = new Floor(eq.get(eq.size()-1).getOutputV(), name);
						eq.add(floor);
					}
					}
					else {
					}
				}
			} else if (matcher1.find()) {
				String name1 = matcher1.group(1) + matcher1.group(2);
				String a1 = matcher1.group(1);
				if (i == 0) {
					if (a1.equals("T")) {
						Equipment con = new Concatenation(this.inputV, name1);
						eq.add(con);
					} else if (a1.equals("M")) {
						Equipment pa = new Parallel(this.inputV, name1);
						eq.add(pa);
					}
				}
				else if(i != 0 && !name1.equals(eq.get(eq.size()-1).getname())) {
					if (a1.equals("T")) {
						Equipment con = new Concatenation(eq.get(eq.size()-1).getOutputV(), name1);
						eq.add(con);
					} else if (a1.equals("M")) {
						Equipment pa = new Parallel(eq.get(eq.size()-1).getOutputV(), name1);
						eq.add(pa);
					}
				}
			}
			
		}
		int j=0;
		for(String key:hm1.keySet()) {
			if(this.eq.get(j).getname().equals(key)) {
				this.eq.set(j, hm1.get(key));
			}
			j++;
		}
		j=0;
		for(Equipment t:eq) {
			for(String key:hm2.keySet()) {
				if(t.getname().equals(key)) {
					this.eq.set(j, hm2.get(key));
				}
				j++;
			}
		}
		
	}
並聯類:
點選檢視程式碼
public void setCon(String str,HashMap<String,Equipment> hm){
    	String test = str.replaceAll("\\[", "").replaceAll("\\]", "");
		String str1[] = str.split(" ");
		for(int i = 0; i < str1.length; i++) {
			Pattern pattern = Pattern.compile("(T[0-9])");
			Matcher matcher = pattern.matcher(str1[i]);
			if (matcher.find()) {
				String name = matcher.group(1);
				Equipment con = new Concatenation(this.inputV, name);
				eq.add(con);
			}
		}
		int j=0;
		for(String key:hm.keySet()) {
			if(this.eq.get(j).getname().equals(key)) {
				this.eq.set(j, hm.get(key));
			}
			j++;
		}
    }
SourceMontor的生成報表如下:

由主要的三個函式和總體來看,不足還是有很多。
根據此報表,以下是改進方向:
減少方法呼叫:方法呼叫可能會導致程式碼執行效率降低。嘗試合併或簡化方法呼叫,以減少方法的呼叫次數。
增加註釋:註釋嚴重不足,有很多的空間來新增註釋,從而提高程式碼的可讀性和維護性。
控制方法複雜度:平均每方法語句數過多,這可能表明某些方法過於複雜。嘗試分解這些方法為更小的、更專注的部分。
管理塊深度:最大塊深度為太大,這可能會影響程式碼的效能。最佳化塊結構,使其更加扁平化,以減少執行時間。
使用介面以簡化程式碼。
類圖如下:

採坑心得:
第四次作業:
多選題答案若為ABCD,作答為AC,直接用contains識別不出部分正確,AB可識別(字串相連才可直接用,否則用多迴圈)。

第五次作業:
注意分檔調速器擋位可加可減
輸出時要按題目要求的順序輸出,不可只按線路順序輸出
在調整開關時,幹路開關斷一個,整個線路斷路
第六次作業:
在並聯提取時,傳前面記錄的串聯電路,以指向同一物件,否則後面開關轉換無法深入到真正的開關
輸出時對每一種電器內部排序
修改狀態時的優先順序按先開關後按線路順序
改進建議:
寫程式碼時提前畫好類圖以減少遺漏。
排序優先於修改狀態更好
多考慮優先順序問題,先將預定狀態確定後再傳電壓
總結:
1.學到了:
Java基礎語法
封裝、繼承、多型的實現(結構體實現封裝,組合實現繼承,介面實現多型)
介面的定義和實現(一個類可以實現多個介面 使用關鍵字interface來定義介面。)
抽象類和介面的區別
ArrayList、LinkedList、HashMap的使用方法
2.需要進一步學習及研究:
在寫程式碼之前,要將整個程式碼的邏輯部分設計好,避免出現寫到中途手忙腳亂的現象。
提升程式碼的可複用性,注重設計中的開閉原則。
每次寫完程式碼都會出現陣列越界的情況,原因大概是思考邏輯時,無法面面俱到。多敲程式碼以提升自己的思考能力以及邏輯能力。畫設計圖、類圖是個不錯的選擇。
在物件傳遞與賦值時找錯物件,畫出空間概念圖可避免
3.建議:
希望能有完整的思路和過程展示,以供觀摩
希望測試點點明測試方向。
希望挑出常用的難點講