最初に、問題クラス(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つの無秩序入力テストケースに問題があります。非ゼロ終了エラーも発生しており、原因の特定が困難です。
使用ツール
- CSDN: 主に学習に使用
- コード検査ツール: 正規表現の検証に役立つ
まとめ
- Javaの便利さと課題の難しさを改めて感じました。クラスは構造体よりも柔軟で使いやすいです。
- Javaのクラス設計と相互利用方法を学びました。ArrayListやHashMapなどを活用することで、データ操作が容易になりました。
- 問題文をよく読むことが重要です。誤解すると再作成が必要になります。
- デバッグツールが非常に役立ちました。IDEAのデバッガはデータ表示と論理エラーの修正に大変役立ちました。
- 正規表現とsplit(), trim()関数の使いこなしは非常に重要です。
この課題は時間をかければ必ず完成できます。第三回の課題が不完全でしたが、粘り強く改善していきます。