三次答題判題程式練習讓你入門Java。

江乐霖發表於2024-04-21

(1)前言

本階段三次題目集涵蓋了從基礎程式設計概念到較複雜演算法設計等多個知識點。題量適中,難度呈梯度上升,從簡單的資料結構與演算法實現到複雜的問題求解,逐步挑戰學生的程式設計能力。

第一次題目集主要考察基本語法、資料型別和簡單的控制結構;第二次題目集則增加了陣列、連結串列等資料結構的應用;到了第三次題目集,題目則涉及到了正規表示式的實現以及動態規劃。整體而言,題目難度逐次遞增,既考察了學生對基礎知識的掌握,也檢驗了他們的分析和解決問題的能力。

(2)設計與分析

一、第一次題目集共有五道題,前四道題比較基礎主要考察類的設計、類和物件的使用、類、陣列的基本運用、關聯類。最後一題是答題判題程式-1綜合了前四道題的技巧。
就最後一題而言:
我首先仔細讀題,考慮到出入格式是嚴格按照順序的(第一行是n;後面n行是question,再下一行是answer,最後一行是end)。
我就一切從簡,先用nextLine讀n,再用nextLine讀n行question,接著用next讀到空格前的answer並用每讀一個答案就迴圈給第i個題判斷答案是否正確。最後讀到end的時候就要輸出了。
輸出則是先列印n行題目與答案,最後一行列印true 或false。
1、讀題功能我寫在Question類當中。將"N:|Q:|A:"都替換成空,再用"#"分割成三個部分,分別是id, Content, StandardAnswer。

點選檢視程式碼
public Question(String input) {
        String tmp = input.replaceAll("N:|Q:|A:","");
        String[] a = tmp.trim().split("#");
        a[1] = a[1].trim();
        this.id = Integer.parseInt(a[1]);
        this.Content = a[2].trim();
        this.StandardAnswer = a[3].trim();
        
    }
2、輸出功能:
點選檢視程式碼
if(a.equals("end")) {
            for (int i = 1; i <= n; i++) {
                for(int j = 0; j < n; j++) {
                    if(question[j].getID() == i)
                        question[j].printInfo();
                }
            }
            for (int i = 1; i <= n; i++) {
                for(int j = 0; j < n; j++) {
                    if(question[j].getID() == i)
                        question[j].isTrue();
                }
                if(i != n) System.out.print(" ");
            }
        }

二、第二次題目集共有四道題,前三道題比較基礎主要考察前一個題目級的內容,以及排序查詢更深層次的類與物件。最後一題是答題判題程式-2綜合了前三道題的技巧。

就最後一題而言:
答題判題程式-2比之答題判題程式-1,新增了試卷資訊,並且存在亂序輸入,Question與Answer(AnswerSheet)與Paper(TestPaper)可能穿插輸入,以及一些試卷的設計是否有100分、答卷分數的統計、問卷中是否答題。

1、考慮到新增了個試卷資訊,對於答題判題程式-1的類的設計明顯不夠用,我就對Question、Answer、Paper三個不同的資訊都採取了設計。Answer中使用了Map和Paper中使用了List。

點選檢視程式碼
class Question {//題目
    private int id;
    private String content;
    private String answer;

    Question() {
        
    }
    Question(int id, String content, String answer) {
        this.id = id;
        this.content = content;
        this.answer = answer;
    }
}
class TestPaper {//問卷
    int id;
    Map<Integer, Integer> scores;//題號對應的標準答案

    TestPaper(int id) {
        this.id = id;
        this.scores = new LinkedHashMap<>();
    }
}
class AnswerSheet {//答卷
    int id;
    List<String> answers;//答題者的答案

    AnswerSheet(int id) {
        this.id = id;
        this.answers = new ArrayList<>();
    }
}

2、考慮到存在亂序輸入的可能,我學習到了startsWith(),透過判斷一行的前三個字元,來儲存相應的資訊,如果是"#N:",則存到questions(題目資訊)(LinkedHashMap)中。如果是"#T:",則存到testPapers(問卷資訊)(ArrayList)中。如果是"#S:",則存到answerSheets(答卷資訊)(ArrayList)中,如果是end,則輸出結果。
該功能我寫到了processInput()中。

點選檢視程式碼
    public void processInput() {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            if (line.equals("end")) {
                break;
            }

            if (line.startsWith("#N:")) {
                String[] parts = line.split("#");
                int id = Integer.parseInt(parts[1].trim().split(":")[1]);
                String content = parts[2].trim().split(":")[1];
                String answer = parts[3].trim().split(":")[1];
                addQuestion(id, content, answer);
                
            } else if (line.startsWith("#T:")) {
                String[] parts = line.split(" ", 2);
                int id = Integer.parseInt(parts[0].trim().split(":")[1]);
                String questionsScores = parts[1];
                addTestPaper(id, questionsScores);
            } else if (line.startsWith("#S:")) {
                String[] parts = line.split("#");
                int id = Integer.parseInt(parts[1].trim().split(":")[1]);
                String answers = line.substring(line.indexOf("#A:"));
                addAnswerSheet(id, answers);
                
            }
        }
    }

3、輸出部分:
先按照Paper順序,判斷是否滿分為100。
再按照Answer順序,在List中從i=0開始,取第i個answerSheets。再從List中從n=0中,取第n個testPapers。如果兩者的id(編號)相等,則說明試卷號有效,再從testPaper中的scores與questions中的answer和answerSheets中的answers(List)做比較,判斷對錯並記分。
該功能在printResults()中實現:

點選檢視程式碼
    public void printResults() {
        for (TestPaper testPaper : testPapers) {
            if (testPaper.getTotalScore() != 100) {
                System.out.println("alert: full score of test paper" + testPaper.id + " is not 100 points");
            }
        }
        for (int i = 0; i < answerSheets.size(); i++) {
            boolean flag = false;
            AnswerSheet answerSheet = answerSheets.get(i);
            for(int n = 0; n < testPapers.size(); n++) {
                TestPaper testPaper = testPapers.get(n);
                if(testPaper.getID() == answerSheet.getID()) {
                    flag = true;
                    Integer count = 0;
                    for (Map.Entry<Integer, Integer> entry : testPaper.scores.entrySet()) {
                        count ++;
                        int questionId = entry.getKey();
                        int score = entry.getValue();
                        if(answerSheet.answers.size() >= count) {
                            String answer = answerSheet.answers.get(count - 1);
                            boolean correct = checkAnswer(questionId, answer);
                            System.out.println(questions.get(questionId).getContent() + "~" + answer + "~" + (correct ? "true" : "false"));
                        } else {
                            System.out.println("answer is null");
                        }
                    }

                    List<Integer> scoresList = new ArrayList<>();
                            for (int j = 0; j < testPaper.scores.size(); j++) {
                        int questionId = testPaper.scores.keySet().stream().toArray(Integer[]::new)[j];
                        String answer = answerSheet.answers.size() > j ? answerSheet.answers.get(j) : "answer is null";
                        int score = checkAnswer(questionId, answer) ? testPaper.scores.get(questionId) : 0;
                        scoresList.add(score);
                    }
                    System.out.println(scoresList.stream().map(Object::toString).collect(Collectors.joining(" ")) + "~" + scoresList.stream().mapToInt(Integer::intValue).sum());
                }
                
            }
            if (flag == false) {
                System.out.println("The test paper number does not exist");
            }
        }
    }

三、第三次題目集共有三道題,前二道題比較基礎主要考察前兩個題目級的內容,以及物件導向程式設計的封裝性以及類的基本使用。最後一題是答題判題程式-3綜合了前兩道題的技巧。

就最後一題而言:
答題判題程式-3比之答題判題程式-2,新增了學生資訊、刪除題目資訊,並且需要實現格式錯誤提示資訊,被刪除的題目提示資訊、題目引用錯誤提示資訊、試卷號引用錯誤提示輸出、學號引用錯誤提示資訊。比前兩個答題判題程式更加複雜。

1、類的設計:
Question中新增了個isexist屬性,判斷題目是否被刪除。AnswerSheet類新增了String Sid(學生id),以及answers從List 改成了Map<Integer, String>。
並且新增了Student類。

點選檢視程式碼
class Student {//學生
    String Sid;
    String name;

    Student(String Sid, String name) {
        this.Sid = Sid;
        this.name = name;
    }

    String getSID() {
        return Sid;
    }
    String getName() {
        return name;
    }
}

2、輸入資訊的獲取:
startsWith()的使用新增了判斷"#X:"和"#D:",前者是獲取學生資訊,後者是刪除題目資訊。

點選檢視程式碼
            else if (line.startsWith("#X:")) {
                String parts = line.split(":")[1];
                String[] stus = parts.split("-");
                for (int i = 0; i < stus.length; i++) {
                    String Sid = stus[i].trim().split(" ")[0];
                    String name = stus[i].trim().split(" ")[1];
                    Student student = new Student(Sid, name);
                    students.put(Sid, student);
                }
            } else if (line.startsWith("#D:")) {
                String parts = line.split("-")[1];
                int questionId = Integer.parseInt(parts.trim());
                questions.get(questionId).setisExist(false);
            }

此外使用了正規表示式判斷了資訊輸入格式是否正確

點選檢視程式碼
            if (line.startsWith("#N:")) {
                Pattern pattern = Pattern.compile("(#N:(\\w+) (#Q:(.+))) (#A:(\\w+))");
                Matcher matcher = pattern.matcher(line);
                if(matcher.find()) {
                    String[] parts = line.split("#");
                    int id = Integer.parseInt(parts[1].trim().split(":")[1]);
                    String content = parts[2].trim().split(":")[1];
                    String answer = parts[3].trim().split(":")[1];
                    boolean isexist = true;
                    addQuestion(id, content, answer, isexist);
                } else {
                    System.out.println("wrong format:" + line);
                }
            }

3、輸出格式:

先按照Paper順序,判斷是否滿分為100。
再按照Answer順序,在List中從i=0開始,取第i個answerSheets。再從List中從n=0中,取第n個testPapers。如果兩者的id(編號)相等,則說明試卷號有效,再從testPaper中的scores中的key(題號)和answerSheets中的answers(Map)做比較,如果questions(題目)中不存在則輸出"non-existent question~0",判為0分;如果answerSheet.answers中有這一題則判斷是否被刪除,如果被刪除則輸出"the question " + questionId + " invalid~0",判為0分,否則判斷對錯並記分;其他情況則說明未作答,輸出"answer is null"。
該功能在printResults()中實現:

點選檢視程式碼
    public void printResults() {
        for (TestPaper testPaper : testPapers) {
            if (testPaper.getTotalScore() != 100) {
                System.out.println("alert: full score of test paper" + testPaper.id + " is not 100 points");
            }
        }
        for (int i = 0; i < answerSheets.size(); i++) {
            boolean flag = false;
            AnswerSheet answerSheet = answerSheets.get(i);
            for(int n = 0; n < testPapers.size(); n++) {
                TestPaper testPaper = testPapers.get(n);
                if(testPaper.getID() == answerSheet.getID()) {
                    flag = true;
                    List<Integer> scoresList = new ArrayList<>();
                    for (Map.Entry<Integer, Integer> entry : testPaper.scores.entrySet()) {
                        int questionId = entry.getKey();
                        int score = entry.getValue();
                        if(!questions.containsKey(questionId)) {
                            System.out.println("non-existent question~0");
                                scoresList.add(0);
                        } else if(answerSheet.answers.containsKey(questionId)) {//答卷中是否答題
                            if(questions.get(questionId).getisExist() == true) {//題目存在
                                String answer = answerSheet.answers.get(questionId);
                                boolean correct = checkAnswer(questionId, answer);
                                System.out.println(questions.get(questionId).getContent() + "~" + answer + "~" + (correct ? "true" : "false"));
                                if(correct == true) {
                                    scoresList.add(score);
                                } else {
                                    scoresList.add(0);
                                }
                            } else {//題目被刪除
                                System.out.println("the question " + questionId + " invalid~0");
                                scoresList.add(0);
                            }
                        } else {//否則answer is null
                            System.out.println("answer is null");
                            scoresList.add(0);
                        }
                    }
                    if(!students.containsKey(answerSheet.getSID())) {
                        System.out.println(answerSheet.getSID() + " not found");
                    } else {
                        Student student = students.get(answerSheet.getSID());
                        System.out.println(student.getSID() + " " + student.getName() + ": " + scoresList.stream().map(Object::toString).collect(Collectors.joining(" ")) + "~" + scoresList.stream().mapToInt(Integer::intValue).sum());
                    }
                }
                
            }
            if (flag == false) {
                System.out.println("The test paper number does not exist");
            }
        }
    }

在分析過程中,我發現自己在實現演算法時過於注重細節而忽略了整體架構的設計,導致程式碼結構不夠清晰。此外,對於一些邊界條件的處理也不夠完善,這需要在今後的程式設計實踐中加以改進。

(3)採坑心得

在原始碼提交過程中,我遇到了幾個常見問題。首先是語法錯誤,這通常是由於對程式語言規則理解不透徹或粗心大意導致的。為了避免這類錯誤,我加強了對語法規則的學習,並在編寫程式碼時更加細心。

其次是邏輯錯誤,這類錯誤往往更難發現和修復。為了解決這個問題,我加強了測試用例的設計和驗證,確保程式碼在各種情況下都能正確執行。

此外,我還發現自己在時間管理和程式碼複用方面存在不足。為了提高效率,我學會了使用版本控制工具來管理程式碼,並嘗試將重複的程式碼封裝成函式或類進行復用。

(4)改進建議

針對上述分析和採坑心得,我提出以下改進建議:

加強基礎知識的學習和理解,尤其是對語法規則和常用資料結構的掌握;
提高問題分析和解決能力,多做一些綜合性題目來鍛鍊自己的思維;
注重程式碼質量和可讀性,遵循良好的程式設計規範,避免過於複雜的程式碼結構;
加強測試和驗證工作,確保程式碼的正確性和穩定性;
學會使用工具來提高程式設計效率和質量,如版本控制工具、程式碼分析工具等。
(5)總結

透過本階段三次題目集的練習,我收穫頗豐。我不僅鞏固了基礎知識,還提高了程式設計能力和問題解決能力。同時,我也認識到自己在程式設計實踐中還存在許多不足之處,需要繼續努力學習和提高。

對於教師、課程、作業等方面的建議,我認為可以增加一些綜合性題目的比重,以更好地檢驗學生的綜合能力。此外,還可以組織一些線上或線下的討論會,讓學生分享自己的程式設計經驗和心得,促進相互學習和交流。Blog就是一個很好的交流渠道,同學們透過閱讀他人的Blog來學習他人的方法,減少自己可能遇到的錯誤。

總之,本階段的題目集對於我的程式設計能力提升起到了很大的幫助作用。我將繼續努力學習和實踐,爭取在下一階段取得更好的成績。

相關文章