三次大作業の振り返りと実装ノウハウ

最初に、問題クラス(Ques)、試験クラス(Test)、解答カードクラス(anscard)、学生クラス(Student)を定義しました。

  • Ques: num(番号), ques(問題文), ans(正解), pd(正誤判定), fen1(配点), fen2(得点), y(状態)
  • Test: num2(試験番号), List<Ques>l(問題リスト), sum(総得点)
  • anscard: num3(試験番号), man(学籍番号), List<Ques>card(解答リスト)
  • Student: name(名前), num(学籍番号)

各クラスのインスタンスを保持するためのリストとして、listQ、listT、listCを用意しました。

入力データはforループで読み込み、正規表現を使用してフォーマットの検証を行いました:

  • 試験データ: "#T:[0-9]+(\s+[0-9]+-[0-9]+)*"
  • 問題データ: "#N:[0-9]+\s+#Q:.+\s+#A:.+"
  • 解答データ: "#S:[0-9]+\s+[0-9]+(\s+#A:[0-9]+-.*)*"
  • 学生データ: "#X:([0-9]+\s+\w+-)*([0-9]+\s+\w+)"
  • エラーデータ: "#D:N-(.+)"

問題データは新しいQuesオブジェクトを作成し、listQに追加します。

試験データはTestオブジェクトに格納され、試験番号が設定されます。その後、splitメソッドを使って問題番号と配点を抽出し、それぞれの要素をString配列に格納します。問題リストに格納する際には、listQから該当する問題を検索します。もし見つからない場合はyを2に設定し、見つかった場合は配点も同時に記録し、総得点sumを更新します。また、試験登録時に総得点を出力します。

解答データはanscardオブジェクトに格納され、正規表現により学籍番号と問題番号・解答を分離します。このとき、解答の順序は試験の問題順序に従う必要があり、これは理解しにくいポイントでした。

学生情報は正規表現と配列・リストを用いて保存しました。

削除処理では、listQおよびlistT内の問題を「noQues」に置き換えました。これにより、後続の処理で「noQues」かどうかを判定することで、エラー出力を簡潔に行えます。

出力処理は重要な部分であり、多くの時間を使いました。特に末尾のスペース処理について、trim()関数の使用方法に悩まされました。他の学生のACコードを確認した結果、trim()を使用することが望ましいと考えました。

問題の状態管理について、以下の条件でy値を設定しました:

  • 未使用: y=1(null状態)、出力: "answer is null"
  • 存在しない: y=2、出力: "no expection..."
  • 削除済み: y=3、出力例: "the question 2 invalid~0"

解答入力と比較処理を同時に行い、得点(fen2)を計算しました。出力時にはfen2の合計を求めるだけで、各学生の総得点を得られます。

問題に関する考察

この課題は2回書き直しましたが、どちらもmainメソッドのみで構成されていました。2回目の作業では一部関数を導入しましたが、初心者としては難しいものでした。他の学生のACコードを参考にしながら反省しました。今後はまず全体方針と構想を立て、問題文をよく読むことが重要です。

第三次の課題は完全には達成できませんでした。テストケースの一部がACせず、次の第四回課題への影響がありますが、改善を続けます。

クラス構造の改善案

主な改善点として、以下のようなメソッドに分離することを検討しました:

  • setQuestion()
  • setTest()
  • setCard()

これにより、mainメソッドが簡潔になり、可読性が向上します。

その他の実装アイデア

試験番号を基準にソートする機能を実装する際に、Comparatorインターフェースを使用しました:

class NumComparator1 implements Comparator<Test> {  
    @Override  
    public int compare(Test p1, Test p2) {  
        return p1.getNum1() - p2.getNum1();  
    }  
}

また、バブルソートによる並び替えも実装しました:

public void paixu(List<MyAns> card){  
    int n = card.size();  
    for (int i = 0; i < n - 1; i++) {  
        for (int j = 0; j < n - i - 1; j++) {  
            if (card.get(j).getMynum() > card.get(j + 1).getMynum()) {  
                MyAns temp = card.get(j);  
                card.set(j, card.get(j + 1));  
                card.set(j + 1, temp);  
            }  
        }  
    }  
}

最終的にはMyAnsクラスを排除し、Quesクラスのみで問題、解答、試験を管理することも可能と考えました。

現在のコードは一部テストケースが通過していません。特に、形式エラー時のメッセージや最後の2つの無秩序入力テストケースに問題があります。非ゼロ終了エラーも発生しており、原因の特定が困難です。

使用ツール

  1. CSDN: 主に学習に使用
  2. コード検査ツール: 正規表現の検証に役立つ

まとめ

  • Javaの便利さと課題の難しさを改めて感じました。クラスは構造体よりも柔軟で使いやすいです。
  • Javaのクラス設計と相互利用方法を学びました。ArrayListやHashMapなどを活用することで、データ操作が容易になりました。
  • 問題文をよく読むことが重要です。誤解すると再作成が必要になります。
  • デバッグツールが非常に役立ちました。IDEAのデバッガはデータ表示と論理エラーの修正に大変役立ちました。
  • 正規表現とsplit(), trim()関数の使いこなしは非常に重要です。

この課題は時間をかければ必ず完成できます。第三回の課題が不完全でしたが、粘り強く改善していきます。

タグ: Java 正規表現 クラス設計 デバッグ アルゴリズム

10月22日 22:27 投稿