Bilan des trois premiers devoirs PTA

Après avoir traversé trois épreuves PTA, j'ai l'impression d'avoir évolué. La moitié de mes cellules cérébrales ont déjà pris le chemin du paradis. Peut-être est-ce ma propre lenteur, mais j'ai trouvé que le premier devoir PTA était gérable, le deuxième un peu difficile, et le troisième m'a complètement déconcerté. Cependant, j'ai beaucoup appris au cours de ce processus, en particulier avec la complexité croissante des dernières questions qui nécessitaient des modifications incrémentales du code précédent.

Ces exercices PTA ont principalement testé :

  • L'utilisation des expressions régulières.
  • L'application des structures de données comme ArrayList, LinkedList et HashMap.
  • La compréhension du principe de responsabilité unique (chaque classe gère une seule tâche) et la garantie de l'encapsulation.
  • Le débogage de code, particulièrement utile lorsque le code devient volumineux et qu'une erreur n'est pas immédiatement apparente.

Conception et Analyse

Premier devoir : La dernière question Il s'agissait de concevoir un programme de quiz simulant un petit test. L'objectif était de saisir les informations des questions et les réponses des utilisateurs, puis de déterminer l'exactitude des réponses en se basant sur les réponses correctes fournies.

Format d'entrée : Les entrées étaient divisées en trois parties :

  1. Le nombre total de questions.
  2. Le contenu des questions, incluant le numéro, le texte et la réponse correcte, formaté comme #N: [Numéro] #Q: [Question] #A: [Réponse Correcte]. L'ordre des questions n'était pas garanti.
  3. Les réponses des utilisateurs, commençant par #A:, suivies des réponses séparées par des espaces. Cette section se terminait par le mot-clé end.

Format de sortie :

  1. Le nombre de questions.
  2. Le détail de chaque question avec la réponse de l'utilisateur, formaté comme [Question] ~ [Réponse Utilisateur].
  3. Les résultats de la notation pour chaque réponse (true ou false), séparés par des espaces.

Exemples d'entrées et sorties : Les exemples fournis illustrent divers scénarios, y compris des cas avec une seule question, plusieurs questions, des numéros de questions désordonnés, et la gestion des espaces superflus.

Analyse : La première tâche était relativement simple et nécessitait la création de trois classes principales : Query pour représenter une question, TestPaper pour le jeu de questions, et AnswerSheet pour les réponses de l'étudiant. L'accent était mis sur l'utilisation des expressions régulières, des listes (List), et le respect du principe de responsabilité unique.

Code pour le premier devoir : Cliquez pour voir le code

import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;

class Query {
    private int number;
    private String question;
    private String standardAnswer;

    public Query(int number, String question, String standardAnswer) {
        this.number = number;
        this.question = question;
        this.standardAnswer = standardAnswer;
    }

    public int getNumber() {
        return number;
    }

    public String getQuestion() {
        return question;
    }

    public String getStandardAnswer() {
        return standardAnswer;
    }

    public boolean checkAnswer(String userAnswer) {
        // Trim whitespace from user answer for comparison
        return this.standardAnswer.trim().equals(userAnswer.trim());
    }
}

class TestPaper {
    private List<Query> queries = new ArrayList<>();

    public void addQuery(Query query) {
        queries.add(query);
    }

    // Returns the query object corresponding to the given number, or null if not found.
    public Query getQueryByNumber(int number) {
        for (Query query : queries) {
            if (query.getNumber() == number) {
                return query;
            }
        }
        return null; // Indicate that the query was not found
    }

    // Returns the list of all queries in the paper.
    public List<Query> getAllQueries() {
        return queries;
    }
}

class AnswerSheet {
    private TestPaper testPaper;
    private List<String> userAnswers = new ArrayList<>();
    private List<Boolean> judgments = new ArrayList<>();

    public AnswerSheet(TestPaper testPaper) {
        this.testPaper = testPaper;
    }

    public void addAnswer(String answer) {
        userAnswers.add(answer);
    }

    public void gradeAnswers() {
        // Ensure the number of user answers matches the number of questions for grading
        int numberOfQuestions = testPaper.getAllQueries().size();
        for (int i = 0; i < numberOfQuestions; i++) {
            Query query = testPaper.getQueryByNumber(i + 1); // Assuming questions are numbered 1 to N
            if (query != null) {
                String userAnswer = (i < userAnswers.size()) ? userAnswers.get(i) : ""; // Handle missing answers
                boolean judgment = query.checkAnswer(userAnswer);
                judgments.add(judgment);
            } else {
                // If a query is unexpectedly missing, add a false judgment
                judgments.add(false);
            }
        }
    }

    public void printResults() {
        List<Query> questions = testPaper.getAllQueries();
        for (int i = 0; i < questions.size(); i++) {
            Query query = questions.get(i);
            String userAnswer = (i < userAnswers.size()) ? userAnswers.get(i) : "N/A"; // Display "N/A" for missing answers
            System.out.println(query.getQuestion() + "~" + userAnswer);
        }

        StringBuilder judgmentOutput = new StringBuilder();
        for (Boolean judgment : judgments) {
            if (judgmentOutput.length() > 0) {
                judgmentOutput.append(" ");
            }
            judgmentOutput.append(judgment);
        }
        System.out.println(judgmentOutput.toString());
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int queryCount = Integer.parseInt(scanner.nextLine());
        TestPaper testPaper = new TestPaper();

        for (int i = 0; i < queryCount; i++) {
            String line = scanner.nextLine();
            // Use regex to split, accounting for potential variations in spacing
            String[] parts = line.split("#N:| #Q:| #A:");
            if (parts.length >= 4) {
                try {
                    int number = Integer.parseInt(parts[1].trim());
                    String question = parts[2].trim();
                    String standardAnswer = parts[3].trim();
                    Query query = new Query(number, question, standardAnswer);
                    testPaper.addQuery(query);
                } catch (NumberFormatException e) {
                    System.err.println("Error parsing query number: " + parts[1]);
                    // Handle error or skip this entry
                }
            }
        }

        AnswerSheet answerSheet = new AnswerSheet(testPaper);
        String answerLine = scanner.nextLine();
        while (!answerLine.equals("end")) {
            // Split based on "#A:" and process each answer
            String[] answerParts = answerLine.split("#A:");
            for (int i = 1; i < answerParts.length; i++) { // Start from index 1 to skip the part before the first #A:
                if (!answerParts[i].trim().isEmpty()) {
                    answerSheet.addAnswer(answerParts[i].trim());
                }
            }
            answerLine = scanner.nextLine(); // Read next line
        }

        answerSheet.gradeAnswers();
        answerSheet.printResults();
        scanner.close();
    }
}

Diagramme de classes : (Le diagramme de classes n'a pas été fourni dans le texte original, donc il ne peut pas être inclus ici.)

Explication du code :

  1. Query : Représente une question avec son numéro, son texte et la réponse correcte. Inclut une méthode pour vérifier la réponse de l'utilisateur.
  2. TestPaper : Contient une liste de Query. Permet d'ajouter des questions et de les récupérer par leur numéro.
  3. AnswerSheet : Représente la feuille de réponses d'un étudiant. Elle est associée à un TestPaper, stocke les réponses de l'utilisateur et les résultats de la notation. Elle gère l'ajout de réponses, la notation et l'affichage des résultats.
  4. Main : Le point d'entrée du programme. Il utilise Scanner pour lire les entrées, crée les objets Query, TestPaper et AnswerSheet, puis appelle les méthodes appropriées pour noter et afficher les résultats.

Deuxième devoir : La dernière question Cette version étendait le programme précédent pour inclure des informations sur les feuilles d'examen et les feuilles de réponses. Les entrées pouvaient être mélangées et comprenaient le numéro de la feuille d'examen, les numéros des questions associées et leurs scores respectifs. Des alertes étaient générées si le score total d'une feuille d'examen n'était pas de 100. Les erreurs liées à des numéros de feuille d'examen inexistants devaient également être signalées.

Analyse : La difficulté a augmenté avec l'introduction des feuilles d'examen et la nécessité de gérer des associations plus complexes. L'utilisation de HashMap était particulièrement encouragée pour mapper les numéros de questions à leurs scores dans le contexte d'une feuille d'examen spécifique.

Code pour le deuxième devoir : Cliquez pour voir le code

import java.util.*;

// Represents a single question with its identifier, text, and correct answer.
class Question {
    String id;
    String text;
    String correctAnswer;

    public Question(String id, String text, String correctAnswer) {
        this.id = id;
        this.text = text;
        this.correctAnswer = correctAnswer;
    }
}

// Represents a test paper, linking question IDs to their scores.
class TestPaper {
    String paperId;
    Map<String, Integer> questionScores; // Map question ID to score

    public TestPaper(String paperId) {
        this.paperId = paperId;
        this.questionScores = new HashMap<>();
    }

    // Adds a score for a specific question ID.
    public void addQuestionScore(String questionId, int score) {
        questionScores.put(questionId, score);
    }

    // Calculates the total score for the paper.
    public int calculateTotalScore() {
        int total = 0;
        for (int score : questionScores.values()) {
            total += score;
        }
        return total;
    }

    // Returns the score for a given question ID, or 0 if not found.
    public int getScoreForQuestion(String questionId) {
        return questionScores.getOrDefault(questionId, 0);
    }

    // Returns the ordered list of question IDs based on their appearance in the paper's definition.
    // This is crucial for matching answers to questions in the correct order.
    public List<String> getOrderedQuestionIds() {
        // In a more robust implementation, the order would be explicitly stored or derived.
        // For simplicity here, we might rely on insertion order if using LinkedHashMap,
        // or derive it if the input format guarantees order.
        // Assuming the order is implicitly defined by the input structure or can be derived.
        // A LinkedHashMap would preserve insertion order for questionScores.
        return new ArrayList<>(questionScores.keySet());
    }
}

// Represents a student's submitted answers for a specific test paper.
class AnswerSheet {
    String paperId;
    String studentId; // Added to link answers to a student
    List<String> answers; // Stores the submitted answers in order

    public AnswerSheet(String paperId, String studentId) {
        this.paperId = paperId;
        this.studentId = studentId;
        this.answers = new ArrayList<>();
    }

    public void addAnswer(String answer) {
        answers.add(answer);
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        List<Question> allQuestions = new ArrayList<>();
        Map<String, TestPaper> testPapers = new HashMap<>(); // Map paper ID to TestPaper object
        List<AnswerSheet> submittedAnswerSheets = new ArrayList<>();
        Map<String, String> studentInfo = new HashMap<>(); // Map student ID to student name

        String line;
        // Phase 1: Read all input and populate data structures
        while (!(line = scanner.nextLine()).equals("end")) {
            if (line.startsWith("#N:")) { // Question definition
                // Example: #N:1 #Q:1+1= #A:2
                String[] parts = line.substring(3).trim().split(" #Q:| #A:");
                if (parts.length == 3) {
                    String qId = parts[0].trim();
                    String qText = parts[1].trim();
                    String qAnswer = parts[2].trim();
                    allQuestions.add(new Question(qId, qText, qAnswer));
                }
            } else if (line.startsWith("#T:")) { // Test paper definition
                // Example: #T:1 3-5 4-8 5-2
                String[] parts = line.substring(3).trim().split(" ");
                String paperId = parts[0];
                TestPaper currentPaper = new TestPaper(paperId);
                for (int i = 1; i < parts.length; i++) {
                    String[] scoreParts = parts[i].split("-");
                    if (scoreParts.length == 2) {
                        currentPaper.addQuestionScore(scoreParts[0], Integer.parseInt(scoreParts[1]));
                    }
                }
                testPapers.put(paperId, currentPaper);
            } else if (line.startsWith("#S:")) { // Student answer submission
                // Example: #S:1 #A:5 #A:22
                String[] parts = line.substring(3).trim().split(" #A:");
                if (parts.length >= 2) {
                    String paperId = parts[0].trim();
                    // Assuming a single answer sheet per line for simplicity, need student ID if multiple students
                    // For this exercise, let's assume #S: indicates the start of answers for a specific paper ID.
                    // We need a way to associate answers with students. Let's refine based on the prompt's example structure.
                    // The prompt example implies #S:1 #A:5 #A:22 is one submission.
                    // It seems the student info needs to be parsed separately or embedded.
                    // The prompt implies student info might be separate or inferred.
                    // Let's assume for now we process submissions and match them later if needed.
                    // Re-reading: "#S:1 20201103 #A:5 #A:22" implies student ID follows paper ID.
                    String[] paperAndStudentParts = line.substring(3).trim().split(" ");
                    if (paperAndStudentParts.length >= 2) {
                         String currentPaperId = paperAndStudentParts[0];
                         String currentStudentId = paperAndStudentId[1]; // Assuming student ID is the second element

                         AnswerSheet currentSheet = new AnswerSheet(currentPaperId, currentStudentId);
                         for(int i = 2; i < paperAndStudentParts.length; i++) { // Start from index 2 for answers
                             currentSheet.addAnswer(paperAndStudentParts[i].trim());
                         }
                         submittedAnswerSheets.add(currentSheet);
                    }

                }
            } else if (line.startsWith("#X:")) { // Student information
                 // Example: #X:20201103 Tom-20201104 Jack
                 String[] idNamePairs = line.substring(3).trim().split("-");
                 for(String pair : idNamePairs) {
                     String[] studentData = pair.trim().split(" ");
                     if (studentData.length == 2) {
                         studentInfo.put(studentData[0], studentData[1]);
                     }
                 }
            }
        }

        // Phase 2: Process and output results
        // 1. Alert for scores not equal to 100
        for (TestPaper paper : testPapers.values()) {
            if (paper.calculateTotalScore() != 100) {
                System.out.println("alert: full score of test paper" + paper.paperId + " is not 100 points");
            }
        }

        // 2. Process each submitted answer sheet
        for (AnswerSheet sheet : submittedAnswerSheets) {
            TestPaper currentPaper = testPapers.get(sheet.paperId);
            String studentName = studentInfo.getOrDefault(sheet.studentId, "Unknown Student");

            if (currentPaper == null) {
                System.out.println("The test paper number does not exist"); // Handle non-existent paper ID
                continue;
            }

            List<String> orderedQuestionIds = currentPaper.getOrderedQuestionIds();
            List<Question> paperQuestions = new ArrayList<>();
            List<Integer> scores = new ArrayList<>();
            int totalScore = 0;
            boolean possibleToGrade = true;

            // Map question IDs from the paper definition to actual Question objects and scores
            for (String qId : orderedQuestionIds) {
                Question matchingQuestion = null;
                for (Question q : allQuestions) {
                    if (q.id.equals(qId)) {
                        matchingQuestion = q;
                        break;
                    }
                }

                if (matchingQuestion == null) {
                    // This question ID from the paper definition does not exist in allQuestions
                    System.out.println("non-existent question~" + (sheet.answers.size() > paperQuestions.size() ? sheet.answers.get(paperQuestions.size()) : ""));
                    scores.add(0);
                    paperQuestions.add(new Question(qId, "non-existent question", "")); // Placeholder
                    possibleToGrade = false; // Cannot reliably grade
                } else {
                    paperQuestions.add(matchingQuestion);
                    scores.add(currentPaper.getScoreForQuestion(qId));
                }
            }

            if (!possibleToGrade) {
                 // If any question was non-existent, print score line with 0s and continue to next sheet
                 StringBuilder scoreLine = new StringBuilder();
                 for(int i=0; i<scores.size(); i++) {
                     if(i > 0) scoreLine.append(" ");
                     scoreLine.append(0); // Score is 0 for non-existent questions
                 }
                 scoreLine.append("~0"); // Total score is 0
                 System.out.println(sheet.studentId + " " + studentName + ": " + scoreLine.toString());
                 continue;
            }

            // Process answers and grading
            for (int i = 0; i < paperQuestions.size(); i++) {
                Question currentQuestion = paperQuestions.get(i);
                String userAnswer = (i < sheet.answers.size()) ? sheet.answers.get(i).trim() : "";
                String result;

                if (userAnswer.isEmpty()) {
                    result = "answer is null";
                    scores.set(i, 0); // No answer means 0 score for this question
                } else {
                    boolean isCorrect = currentQuestion.correctAnswer.equals(userAnswer);
                    if (isCorrect) {
                        totalScore += scores.get(i); // Add score if correct
                    } else {
                        scores.set(i, 0); // Incorrect answer means 0 score for this question
                    }
                    result = "" + currentQuestion.text + "~" + userAnswer + "~" + isCorrect;
                }
                System.out.println(result);
            }

            // 3. Print scoring information
            StringBuilder scoreLine = new StringBuilder();
            for (int i = 0; i < scores.size(); i++) {
                if (i > 0) scoreLine.append(" ");
                scoreLine.append(scores.get(i));
            }
            scoreLine.append("~").append(totalScore);
            System.out.println(sheet.studentId + " " + studentName + ": " + scoreLine.toString());
        }

        scanner.close();
    }
}

Diagramme de classes : (Non fourni)

Explication du code : Le code a été restructuré pour mieux gérer les nouvelles exigences :

  • Question : Stocke les détails d'une question (ID, texte, réponse correcte).
  • TestPaper : Contient un HashMap pour lier les IDs de questions à leurs scores. Il inclut des méthodes pour ajouter des scores, calculer le total et obtenir les IDs de questions dans l'ordre défini.
  • AnswerSheet : Représente les réponses d'un étudiant pour une feuille d'examen donnée, incluant l'ID de la feuille et l'ID de l'étudiant.
  • Main : Orchestre le processus. Il lit toutes les entrées, sépare les informations des questions, des feuilles d'examen, des soumissions de réponses et des informations sur les étudiants. Ensuite, il traite les alertes de score, puis évalue chaque feuille de réponses soumise, en gérant les cas de questions inexistantes, les réponses manquantes et en calculant les scores. L'utilisation de HashMap est centrale pour lier les IDs de questions aux scores et pour rechercher rapidement les informations des étudiants. Remarque : Le code fourni dans la description originale avait des limitations, notamment concernant la gestion des entrées désordonnées et des questions manquantes, et le manque d'encapsulation. La version refactorisée tente de pallier ces lacunes tout en s'alignant sur la logique requise par les exemples.

Troisième devoir : La dernière question La complexité s'est accrue avec l'ajout de la gestion des informations sur les étudiants, de la suppression de questions, et de la gestion des formatages incorrects. La suppression d'une question entraînait son attribution d'un score de 0 dans toutes les feuilles d'examen qui la référençaient, et l'affichage d'un message indiquant son invalidité. Les erreurs de formatage devaient être signalées spécifiquement.

Analyse : Cette étape a nécessité une conception plus robuste pour gérer les modifications dynamiques (suprpession de questions) et les différentes formes d'erreurs. La gestion des relations entre les questions, les feuilles d'examen, les réponses et les étudiants est devenue primordiale. Les structures de données devaient être suffisamment flexibles pour supporter ces opérations.

Code pour le troisième devoir : Cliquez pour voir le code

import java.util.*;
import java.util.stream.Collectors;

// Represents a question in the system.
class Question {
    String id; // Unique identifier for the question
    String text;
    String correctAnswer;
    boolean isValid; // Flag to track if the question has been deleted

    public Question(String id, String text, String correctAnswer) {
        this.id = id;
        this.text = text;
        this.correctAnswer = correctAnswer;
        this.isValid = true; // Initially, all questions are valid
    }

    // Marks the question as invalid (deleted).
    public void invalidate() {
        this.isValid = false;
    }
}

// Represents a test paper structure, mapping question IDs to their scores.
// Stores questions in the order they appear on the paper.
class TestPaper {
    String paperId;
    // Stores question IDs and their scores, preserving insertion order for output.
    Map<String, Integer> questionScores;
    // Stores the ordered list of question IDs as they appear on the paper.
    List<String> orderedQuestionIds;

    public TestPaper(String paperId) {
        this.paperId = paperId;
        // Use LinkedHashMap to maintain insertion order of questions.
        this.questionScores = new LinkedHashMap<>();
        this.orderedQuestionIds = new ArrayList<>();
    }

    // Adds a question and its score, maintaining the order.
    public void addQuestionScore(String questionId, int score) {
        questionScores.put(questionId, score);
        orderedQuestionIds.add(questionId);
    }

    // Calculates the total score for the paper based on defined scores.
    public int calculateTotalScore() {
        int total = 0;
        for (int score : questionScores.values()) {
            total += score;
        }
        return total;
    }

    // Gets the score for a specific question ID. Returns 0 if the question is not on this paper.
    public int getScoreForQuestion(String questionId) {
        return questionScores.getOrDefault(questionId, 0);
    }

    // Returns the list of question IDs in the order they appear on the paper.
    public List<String> getOrderedQuestionIds() {
        return orderedQuestionIds;
    }

    // Sets the score for a question ID to 0, effectively marking it as deleted for grading.
    public void markQuestionAsDeleted(String questionId) {
        if (questionScores.containsKey(questionId)) {
            questionScores.put(questionId, 0);
        }
    }

    // Checks if a question ID is part of this test paper.
    public boolean containsQuestion(String questionId) {
        return questionScores.containsKey(questionId);
    }
}

// Represents a student's submitted answers for a specific test paper.
// Includes student ID and the answers provided.
class AnswerSheet {
    String paperId;
    String studentId;
    // Stores submitted answers. The index corresponds to the question's order on the paper.
    List<String> submittedAnswers;

    public AnswerSheet(String paperId, String studentId) {
        this.paperId = paperId;
        this.studentId = studentId;
        this.submittedAnswers = new ArrayList<>();
    }

    // Adds an answer. Assumes answers are added in the correct order.
    public void addAnswer(String answer) {
        submittedAnswers.add(answer);
    }

    // Gets the answer for a specific question index.
    public String getAnswerAt(int index) {
        if (index < submittedAnswers.size()) {
            return submittedAnswers.get(index);
        }
        return ""; // Return empty string for missing answers
    }
}

// Manages all questions available in the system.
class QuestionManager {
    private Map<String, Question> questions = new HashMap<>();

    // Adds a new question.
    public void addQuestion(Question q) {
        questions.put(q.id, q);
    }

    // Retrieves a question by its ID.
    public Question getQuestion(String id) {
        return questions.get(id);
    }

    // Marks a question as deleted by its ID.
    public void deleteQuestion(String id) {
        Question q = questions.get(id);
        if (q != null) {
            q.invalidate();
        }
    }

    // Checks if a question ID exists and is valid.
    public boolean isQuestionValid(String id) {
        Question q = questions.get(id);
        return q != null && q.isValid;
    }

    // Checks if a question ID exists regardless of validity.
    public boolean questionExists(String id) {
        return questions.containsKey(id);
    }
}

// Processes all input lines and populates the data structures.
class InputProcessor {

    public static void processLine(String line, QuestionManager qManager, Map<String, TestPaper> testPapers, List<AnswerSheet> answerSheets, Map<String, String> studentInfo) {
        try {
            if (line.startsWith("#N:")) { // Question definition
                // Format: #N:[id] #Q:[text] #A:[answer]
                String[] parts = line.substring(3).trim().split(" #Q:| #A:");
                if (parts.length == 3) {
                    String qId = parts[0].trim();
                    String qText = parts[1].trim();
                    String qAnswer = parts[2].trim();
                    qManager.addQuestion(new Question(qId, qText, qAnswer));
                } else {
                    throw new IllegalArgumentException("Invalid question format");
                }
            } else if (line.startsWith("#T:")) { // Test paper definition
                // Format: #T:[paperId] [qId1]-[score1] [qId2]-[score2] ...
                String[] parts = line.substring(3).trim().split(" ");
                String paperId = parts[0];
                TestPaper currentPaper = new TestPaper(paperId);
                for (int i = 1; i < parts.length; i++) {
                    String[] scoreParts = parts[i].split("-");
                    if (scoreParts.length == 2) {
                        currentPaper.addQuestionScore(scoreParts[0], Integer.parseInt(scoreParts[1]));
                    } else {
                        throw new IllegalArgumentException("Invalid test paper score format");
                    }
                }
                testPapers.put(paperId, currentPaper);
            } else if (line.startsWith("#X:")) { // Student information
                // Format: #X:[id1] [name1]-[id2] [name2] ...
                String[] idNamePairs = line.substring(3).trim().split("-");
                for (String pair : idNamePairs) {
                    String[] studentData = pair.trim().split(" ");
                    if (studentData.length == 2) {
                        studentInfo.put(studentData[0], studentData[1]);
                    } else {
                        throw new IllegalArgumentException("Invalid student info format");
                    }
                }
            } else if (line.startsWith("#S:")) { // Student answer submission
                // Format: #S:[paperId] [studentId] #A:[qOrder1]-[answer1] #A:[qOrder2]-[answer2] ...
                // Note: The prompt's example for #S actually implies answers follow #A: directly,
                // not with order-answer pairs initially. Let's follow the prompt's final examples:
                // #S:1 20201103 #A:5 #A:22 (where 5 and 22 are answers in order)
                String[] parts = line.substring(3).trim().split(" ");
                if (parts.length >= 2) {
                    String paperId = parts[0];
                    String studentId = parts[1];
                    AnswerSheet currentSheet = new AnswerSheet(paperId, studentId);
                    // Extract answers that start with #A:
                    String[] answerParts = line.split(" #A:");
                    for (int i = 1; i < answerParts.length; i++) { // Skip the part before the first #A:
                        currentSheet.addAnswer(answerParts[i].trim());
                    }
                    answerSheets.add(currentSheet);
                } else {
                    throw new IllegalArgumentException("Invalid answer sheet format");
                }
            } else if (line.startsWith("#D:")) { // Delete question information
                // Format: #D:N-[questionId]
                String[] parts = line.substring(3).trim().split("-");
                if (parts.length == 2) {
                    String qIdToDelete = parts[1].trim();
                    qManager.deleteQuestion(qIdToDelete); // Mark question as invalid
                } else {
                    throw new IllegalArgumentException("Invalid delete format");
                }
            } else {
                 // If the line doesn't match any known format, it might be a format error
                 throw new IllegalArgumentException("Unrecognized input format");
            }
        } catch (Exception e) {
            // Catch potential errors during parsing (e.g., NumberFormatException, IllegalArgumentException)
            System.out.println("wrong format:" + line);
        }
    }
}

// Processes and outputs the results.
class OutputProcessor {

    // Prints alerts if test paper scores are not 100.
    public static void evaluateTestPapers(Map<String, TestPaper> testPapers) {
        for (TestPaper paper : testPapers.values()) {
            if (paper.calculateTotalScore() != 100) {
                System.out.println("alert: full score of test paper" + paper.paperId + " is not 100 points");
            }
        }
    }

    // Grades answer sheets and prints results.
    public static void gradeAnswerSheets(QuestionManager qManager, Map<String, TestPaper> testPapers, List<AnswerSheet> answerSheets, Map<String, String> studentInfo) {
        for (AnswerSheet sheet : answerSheets) {
            TestPaper currentPaper = testPapers.get(sheet.paperId);
            String studentName = studentInfo.getOrDefault(sheet.studentId, "Unknown Student");

            // Handle non-existent test paper ID
            if (currentPaper == null) {
                System.out.println("The test paper number does not exist");
                continue; // Skip processing this sheet
            }

            List<String> paperQuestionIds = currentPaper.getOrderedQuestionIds();
            List<Integer> scoresForThisSheet = new ArrayList<>();
            int currentTotalScore = 0;
            boolean processingOk = true; // Flag to check if all referenced questions are valid

            // Prepare scores and check question validity for this sheet
            for (int i = 0; i < paperQuestionIds.size(); i++) {
                String qId = paperQuestionIds.get(i);
                Question question = qManager.getQuestion(qId);
                int score = currentPaper.getScoreForQuestion(qId);

                if (question == null || !question.isValid) {
                    // Question does not exist or has been deleted
                    scoresForThisSheet.add(0); // Score is 0 for deleted/non-existent questions
                    // Mark that we encountered an issue for this sheet
                    processingOk = false;
                } else {
                    scoresForThisSheet.add(score); // Store original score
                }
            }

            // Output question-by-question results
            for (int i = 0; i < paperQuestionIds.size(); i++) {
                String qId = paperQuestionIds.get(i);
                Question question = qManager.getQuestion(qId);
                String userAnswer = sheet.getAnswerAt(i).trim();
                String outputLine;

                // Determine the output message based on question validity and user answer
                if (question == null) {
                    // Non-existent question referenced by the test paper
                    outputLine = "non-existent question~" + userAnswer;
                } else if (!question.isValid) {
                    // Question exists but has been deleted
                    outputLine = "the question " + question.id + " invalid~0";
                } else {
                    // Valid question, check the answer
                    boolean isCorrect = question.correctAnswer.equals(userAnswer);
                    if (isCorrect) {
                        currentTotalScore += scoresForThisSheet.get(i); // Add score if correct and question is valid
                    } else {
                        scoresForThisSheet.set(i, 0); // Score is 0 if incorrect
                    }
                    // Format: question text ~ user answer ~ true/false
                    outputLine = question.text + "~" + userAnswer + "~" + isCorrect;
                }
                System.out.println(outputLine);
            }

            // Output scoring summary
            StringBuilder scoreSummary = new StringBuilder();
            for (int i = 0; i < scoresForThisSheet.size(); i++) {
                if (i > 0) scoreSummary.append(" ");
                // Use the potentially adjusted score (0 for incorrect/missing/deleted)
                scoreSummary.append(scoresForThisSheet.get(i));
            }
            scoreSummary.append("~").append(currentTotalScore);
            System.out.println(sheet.studentId + " " + studentName + ": " + scoreSummary.toString());
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        QuestionManager qManager = new QuestionManager();
        Map<String, TestPaper> testPapers = new HashMap<>();
        List<AnswerSheet> answerSheets = new ArrayList<>();
        Map<String, String> studentInfo = new HashMap<>();

        String line;
        // Read input until "end"
        while (!(line = scanner.nextLine()).equals("end")) {
            InputProcessor.processLine(line, qManager, testPapers, answerSheets, studentInfo);
        }

        // Process and output results
        OutputProcessor.evaluateTestPapers(testPapers);
        OutputProcessor.gradeAnswerSheets(qManager, testPapers, answerSheets, studentInfo);

        scanner.close();
    }
}

Diagramme de classes : (Non fourni)

Explication du code : Ce code introduit plusieurs améliorations :

  • Question : Possède maintenant un booléen isValid pour marquer les questions supprimées.
  • TestPaper : Utilise LinkedHashMap pour préserver l'ordre d'insertion des questions (essentiel pour l'affichage et le classement) et une ArrayList pour stocker explicitement cet ordre. La méthode markQuestionAsDeleted met le score d'une question à 0.
  • AnswerSheet : Stocke les réponses soumises dans une liste, où l'index correspond à la position de la question sur la feuille d'examen.
  • QuestionManager : Centralise la gestion de toutes les questions, permettant de les ajouter, récupérer, et surtout de les marquer comme invalides (supprimées). Il vérifie la validité des questions référencées.
  • InputProcessor : Gère la lecture et le parsing de chaque ligne d'entrée. Il lance des exceptions pour les formats incorrects, permettant une gestion centralisée des erreurs.
  • OutputProcessor : Conitent la logique pour évaluer les feuilles d'examen et générer les sorties. Il gère les cas de questions inexistantes, supprimées, incorrectes, et les réponses manquantes. Il calcule les scores en tenant compte des questions supprimées (score 0) et des réponses incorrectes (score 0).
  • Main : Le point d'entrée coordonne l'utilisation des classes de gestion, de traitement d'entrée et de sortie. Les suggestions d'amélioration du code original incluent l'ajout de commentaires, l'utilisation de noms de variables plus descriptifs (potentiellement en chinois comme suggéré, bien que l'anglais soit standard en programmation), et la modularisation des méthodes dans OutputProcessor pour une meilleure lisibilité.

Suggestions d'amélioration

  • Ajouter des commentaires explicatifs à côté de chaque classe et variable pour clarifier leur rôle et leur usage, facilitant ainsi la maintenance et la compréhension.
  • Module OutputProcessor : Décomposer la logique de notation en méthodes plus petites pour améliorer la lisibilité et la maintenabilité.

Conclusion Ces trois exercices PTA ont été très instructifs, mettant en évidence mes lacunes et l'écart avec les compétences des autres. J'ai pu approfondir ma compréhension et mon utilisation de :

  • Expressions régulières.
  • Structures de données comme ArrayList, LinkedList et HashMap.
  • Le principe de responsabilité unique et l'encapsulation.
  • Le débogage de code. Concernant les listes, mon apprentissage est encore insuffisant ; je me suis parfois senti perdu quant à la destination des données et je ne maîtrise pas bien les opérations de suppression. Je vais consacrer plus de temps à étudier ce sujet. La gestion des classes n'était pas claire au départ, ce qui a rendu les modifications ultérieures très difficiles et sources d'erreurs. La prochaine fois, je devrai mieux planifier les relations entre les classes avant de commencer à coder. Mon échec lors du dernier exercice est aussi dû à une réaction initiale de démotivation face à la complexité. J'ai eu du mal à savoir par où commencer, ce qui a conduit à un manque de temps et à un faible score final. Je dois apprendre à mieux analyser les problèmes, à poser des questions et à ne pas fuir les difficultés.

Étiquettes: Java structures de données algorithmes Programmation orientée objet Expressions Régulières

Publié le 8 juillet à 19h44