答題判題程式分析1

KQRTYYY發表於2024-10-26

一、前言
對於這幾周開始的Java答題判題程式的編寫對我壓力有點大,因為我對程式設計感覺總是慢半拍。雖然痛苦但也學習到了一些知識,過程艱辛但也有些許收穫,讓我對Java程式設計瞭解到了更多
這三次題目集的難度是逐漸向上攀升的,第一次題目集作業除了最後一題大作業都是入門級Java程式設計題讓我們體會Java的程式設計理念,非常詳細的幫助我們建立類,教導我們進行Java入門,構造一個無參構造方法,一個有參構造方法,還有一些get()與set()方法來對類裡面的值進行使用。幫助我進行有關類與物件的應用。
對於大作業題目集1~3的最後一題每次都是難度進行飆升(起碼對我來說)導致我每次畫長時間在編寫最後一天大作業,對我感覺最難的點在於對Java接觸時間短,對於很對函式和方法運用不熟練,但繼續學習下去會感覺到Java對很多方法進行直接封裝了不需要寫一些底層邏輯比如連結串列直接用import java.util.ArrayList;類包裡面進行 ArrayList XXXX就能建立連結串列,還有可以利用import java.util.HashMap;裡面的Map<xxx, xxx> XXX = new LinkedHashMap<>();透過對鍵值對值進行儲存查詢資料等等,但由於我對這些方法不熟悉導致程式設計的時候困阻很大,尤其加上對題目不同情況進行不同的處理,對我每次進行修改程式碼造成很大難題,道阻且長啊!
感覺題量上感覺還是還可以,就是最後一次比較費時間,還有就是最後一題大作業佔分很高,如果最後一題沒時間完成直接影響整體。

二、設計與分析
7-1設計一個風扇類(class Fan)
我認為本次實驗主要是學習到了構造方法
1.一個無參構造方法Fan();
2。一個有參構造方法 public Fan(int fanSpeed,boolean fanOn,double fanRadius,String fanColor) { ... },
3.toString()的方法返回描述風扇的字串。
7-2類和物件的使用
與以上相同
先構造無參方法public Student()
再構造有參方法Student(String name,String sex,String studentID,int age,String major)
再toString()的方法返回描述風扇的字串。
7-3成績計算-1-類、陣列的基本運用
這個程式在7-2上修改,最大收穫是對浮點數保留小數
利用String.format("%.2f",average)來對average保留兩位小數(四捨五入)
7-4成績計算-2-關聯類
與7-3相類似進一步對保留小數進行理解
7-5答題判題程式-1
輸入格式:
程式輸入資訊分三部分:
對於第一次大作業的輸入要求主要分為三個方面:
1、題目數量
格式:整數數值,若超過1位最高位不能為0,
2、題目內容
一行為一道題,可以輸入多行資料。
格式:"#N:"+題號+" "+"#Q:"+題目內容+" "#A:"+標準答案
格式約束:題目的輸入順序與題號不相關,不一定按題號順序從小到大輸入。
3、答題資訊
答題資訊按行輸入,每一行為一組答案,每組答案包含第2部分所有題目的解題答案,答案的順序號與題目題號相對應。
格式:"#A:"+答案內容
格式約束:答案數量與第2部分題目的數量相同,答案之間以英文空格分隔。
答題資訊以一行"end"標記結束,"end"之後的資訊忽略。
很清晰的可以建立三個類,對於我建立的三個類
class Quest
主要是儲存題目資訊主要包括
public Quest(int id, String content, String standardAnswer) {
this.id = id;//題目id號
this.content = content;/題目內容
this.standardAnswer = standardAnswer;//題目答案}
以及一個判斷答案對錯
public boolean judgeAnswer(String answer) {
return standardAnswer.equals(answer.trim());}
還有就是一些set()以及get()函式便於對題目資訊的取值與儲存就不一一展示了。

class Paper
主要就是為了用於封裝整套題目的資訊,透過class Quest建立的問題進行儲存以及處理
採用 List<Quest> questions = new LinkedList<>()一個連結串列對題目資訊進行封裝
public void addQuestion(Quest question) 
這個方法主要是對儲存的問題進行新增到question連結串列中儲存
public boolean checkAnswer(int num, String answer) 
這個方法來對對應編號的題目答案進行判斷,並將結果返回儲存起來方便後續進行驗證,
public Quest getQuestionById(int num) 
  這個方法主要用來判斷獲取題目編號方便進行對題目的判斷,如果該題目不存在則會將該位置賦值null,後續程式將會對改題目不同賦值進行相應的處理
public int getMaxQuestionNumber() 
該函式主要來返回連結串列中儲存的問題個數,方便對迴圈次數以及一些處理進行約束,以便對輸出處理不會因為漏算還是多算。

class AnswerSheet
用於封裝答題資訊,輸出相應結果
private Paper paper;
對封裝好的試卷類進行引用
private List<String> answers = new LinkedList<>();
答案連結串列對題目標準答案進行儲存以便對學生答案進行判斷
private List<Boolean> results = new LinkedList<>();
題目答案和標準答案比對完後將true或者false儲存在results連結串列中
public AnswerSheet(Paper paper) 
對試卷的匯入
public void saveAnswer(int num, String answer)
對相應題目編號答案進行儲存
public void print() 
對題目完成比對後進行對答案的輸出以及相應的提示最後的得分等結果。
對於main函式部分我採用
int questionCount = Integer.parseInt(scanner.nextLine());
透過scanner.nextLine()獲取有多少題目輸入,再將其轉化成int型別的資料儲存起來
在透過questionCount的值new Question()將題目儲存
後續再利用scanner.hasNextLine()對後續的輸入進行處理
      while (scanner.hasNextLine()) {
        String answerInput = scanner.nextLine();
        if ("end".equalsIgnoreCase(answerInput)) {
            break;}
透過scanner.hasNextLine()進行判斷將一次輸入一行在進行處理
  透過每一行輸出的是startsWith("N:")還是startsWith("Q:") 還是startsWith("A:")進行相應的處理
  判斷到這是問題資訊首先進行分割字元split(":")等,new Question(id,content,answer)進行儲存
  String[] answers = new String[parts.length];來儲存答案
  再建立AnswerSheet answerSheet = new AnswerSheet(paper);
  然後儲存答案資訊  answerSheet.saveAnswer(questionNumber, answer);
  最後輸出answerSheet.print();

下面是這次大作業的類圖以及順序圖
image

image

第二次大作業最後一題(小題就不加以贅述了)相對於第一次大作業最後一題增加了試卷資訊將#T 題目編號應與題目資訊中的編號對應。一行資訊中可有多項題目編號與分值。而且輸入順序也會發生改變,答卷資訊也需要增加相應的題目分數,對輸出要求進一步增加,對答卷總分也要求輸出,並且增加了對程式碼健壯性的要求,要求如果輸入的答案資訊少於試卷的題目數量,沒有答案資訊的題目輸"answer is null",如果答卷分值不足100分也要進行輸出提示,alert: full score of test paper1 is not 100 points,而且可能一次輸入兩張試卷資訊,完成答題,要有更多的方法對輸入進行處理,第二次大作業在第一次大作業基礎上增加一部分
但在question類部分沒有改變。
在paper部分增加了變數score與totalScore便於儲存分數
為了便於查詢增加了
private Map<Integer, Question> questionMap = new LinkedHashMap<>();
private Map<Integer, Integer> questionScores = new LinkedHashMap<>();
一個用來儲存問題,一個用來儲存得分情況
public void setPaperID(int paperID) {
this.paperID = paperID; }
增加paperID便於處理多張答題的情況,以便處理相對應的paper。
Anwers部分增加了更多對答案的判斷,尤其是對分數還有題目的結果輸出
results.add(output);
resultScores.add(score);
totalScore += score;等等
本次的類圖以及順序圖
image
image

第三次大作業最後一題(小題就不加以贅述了)相對於第二次難度更大多增加了格式的檢測,這就要求對正規表示式的使用要更加熟練。由於前兩次沒有自己設計類圖,而且僅僅憑著題目的建議和自己的感覺硬寫,導致寫到後期思路很亂,各個類之間的關係不明瞭,也沒做到單一職責,這導致後期程式碼量上來以後,每次開始寫自己程式碼前還要浪費很多時間去回想這些程式碼的作用。最後到處縫縫補補導致自己越來越胡亂,程式碼越來越不清晰,最後完成效果也不怎麼樣。
第三次大作業增加刪除題目資訊和學生資訊
學生資訊只輸入一行,一行中包括所有學生的資訊,每個學生的資訊包括學號和姓名,格式如下。
格式:"#X:"+學號+" "+姓名+"-"+學號+" "+姓名....+"-"+學號+" "+姓名
刪除題目資訊為獨行輸入,每一行為一條刪除資訊,多條刪除資訊可分多行輸入。該資訊用於刪除一道題目資訊,題目被刪除之後,引用該題目的試卷依然有效,但被刪除的題目將以0分計,同時在輸出答案時,題目內容與答案改為一條失效提示,例如:”the question 2 invalid~0”
對於檢測到line.startsWith("#D:N-")需要刪除題目是,我是透過對Question類裡面的id進行賦值為-1;進而使其標記為刪除的題目,分數進行為0處理。
最後一題加入了一個學生類Student,
為了更好的處理這些資訊我建立了更多連結串列以及HashMap來對問題的資訊、答卷資訊、學生資訊和警告資訊等等處理,以及對警告資訊的儲存以方便後續進行處理,方便輸出相應的警告,集中處理。
Map<Integer, Paper> papers = new HashMap<>();
Map<String, AnswerSheet> answerSheets = new HashMap<>();
Map<Integer, Question> questionBank = new HashMap<>();
List alerts = new ArrayList<>(); // 收集警告資訊
List inputs = new ArrayList<>();
Map<String, Student> students = new HashMap<>();
為了集中處理資訊,輸入也需要集中輸入一次性輸入所有資訊將其儲存在List inputs = new ArrayList<>()連結串列中便於集中處理
// 讀取所有輸入
while (!(input = scanner.nextLine()).equals("end")) {
inputs.add(input);}
下面是第三次大作業的類圖以及順序圖
image

image

三、採坑心得
1.讀題要仔細
這是我做大作業首要的收穫,讀題仔細能減少不必要的麻煩,對題目的輸入需要敏感一點,就像第二次大作業的最後一題,他#S答案對應的是輸入順序,而不是第一個答案對應第一題,第二個答案對應第二題,如果輸入的時候採用的是亂序輸入那就會導致不同的題目對應的標準答案出錯,導致得不到對應的輸出。但需要注意的是,第三次大作業最後一題的#S的答案又有了對應的題目標號,需要進行索引,這就導致第三題不能直接複用第二題的基礎,需要進行修改邏輯以及程式碼。還有類似很多,我就因為讀題不仔細,導致有時候需要畫長時間進行對程式碼改錯,時間長了對自己邏輯以及程式設計有直接影響,會導致邏輯不是那麼清晰,又需要進行邏輯梳理,這將導致進行惡性迴圈,也會打擊一點自信心。
2.寫程式碼需要多寫註釋
由於我對Java程式設計能力沒有那麼強,每次完成大作業的最後一題都要花費長時間,通常不能夠一次完成作業,都需要花費好幾天,這個過程寫註釋就非常需要了,通常調侃說:不寫註釋的程式碼只有自己本人和上帝看得懂。但只要三天後就只有上帝看到的懂了。這句話雖然有點玄學,但確實是程式設計人的實際情況,每次我寫寫程式碼把戰線拉長的時候,一旦忘了寫註釋就一朝回到解放前,又要重新開始捋捋思路。也會浪費長時間,導致程式碼不連貫,最好還是能夠一口氣將程式碼完成。寫註釋的時候最好的是能將每一個變數的作用,每一個方法的作用,每一個程式碼塊的作用都要給出註釋,因為當程式碼量大起來後,你可以透過註釋去了解過去的自己寫這些程式碼的目的,而不用去逐條逐條的看程式碼,逐條逐條看程式碼是非常恐怖了。所以寫程式碼加註釋是非常重要的。
3.畫一下順序圖與類圖的必要性
畫順序圖能幫助我們更好理清思路,你寫程式碼的時候可以先構造一個大概的框架,然後在這個框架的基礎上進行畫順序圖,透過畫順序圖的過程能夠發現很多自己欠缺 考慮的方面,能讓自己對框架逐漸完善。然後再透過順序圖完成相應的類圖,最後進行編寫程式碼。雖然畫順序圖和類圖會比較花費時間,但如果時間充足的話,能對自己寫程式碼有很好的幫助。
4.要勇於學習新知識
我對於未知的新東西就會有一點畏懼,對Java裡面很多方法都沒有好好認真學進去,只是那種陌生感讓人感覺不太自信,就比如正規表示式,我就覺得學起來很煩,能不用正規表示式我就會不用,總覺得自己學不會,但看了同學的程式碼發現如果使用正規表示式就會簡單很多,我也慢慢強迫自己去學正規表示式,雖然學的比較慢,但總算學到了一點。要 跳出舒適圈,勇於學習新知識。
四、改進建議:對相應題目的編碼改進給出自己的見解,做到可持續改進
對程式設計的類的封裝很重要,最好對類分的細緻一點,這樣能在下一次大作業的時候能更好的複用,就比如一個類最好就只完成一件事,輸入就輸入,輸出就輸出,越簡單後面就能越能複用,就能減輕下一次程式碼的程式碼量,我寫程式碼能力沒那麼強就設定了四個類,函式也寫的不多,導致我每次大作業都對程式碼有長時間的修改。

五、總結
在本階段三次題目集的學習過程中,我獲得了許多寶貴的經驗和技術提升。從基礎的Java語法到類的設計,再到物件導向程式設計思想的理解,每一步都讓我對Java有了更深的認識。透過這些練習,我不僅掌握了構造方法、toString方法的使用,還學會了如何處理資料格式,比如對浮點數的精確控制。還學習到很多新方法類似與Java裡面的連結串列,hashmap還有正規表示式等等,提升了我的程式設計能力。同時,我也深刻認識到了程式碼註釋的重要性,良好的註釋不僅能幫助他人理解程式碼邏輯,也能在未來自我回顧時提供極大的便利。我也越來越深刻體會到了類與物件的使用。此外,還增強了我的自主學習能力。面對不熟悉的正規表示式,我學會了如何有效地查詢資料並解決問題。
在建議方面,我認為目前的課程安排對我們學生來說是一個不小的挑戰。雖然每次作業給予了一週的時間來完成,但是實際上,由於大三課程安排緊密,我們可用於Java程式設計的時間十分有限。如果把所有課餘時間都投入到Java的學習中,恐怕也無法按時完成所有的任務,因為我們還需要兼顧其他科目的作業。
而對於線上線下課堂的改進方面,我認為上課過程中和學生的互動是很有必要的,對於一些抽象難懂的知識,如果透過簡單程式碼例子的方式實現,我們可能理解得更快。

相關文章