第 3 章 提案システム 14
4.2 システムの内部構成
4.2.2 自動正誤判定システム
概要
自動正誤判定システムはユーザの書いたプログラムが問題文に沿って正しく書かれたものかをテストデー タを用いて判定するシステムである. このシステム全体の流れは下の図4.2のようになっている.
図 4.2: 自動正誤判定の概要 以下では図4.2のそれぞれの項目について説明していく.
第4章 実装 30 テストデータの読み込み&比較回数の確認
テストデータの読み込みを行う. 以下にそのソースコードを表示する. 4.2.3節で説明するテーブルを用 いて問題番号に対応したテストデータを取り出す. それと同時に取り出した回数を記録させることで,比較 回数の確認を行っている. 全てのテストデータが取り出されると次の「コンパイル」に移行する.
1 : l o c a l ( $ q u e s t i o n _ n u m b e r ) = @_ ; 変 数 に 問 題 番 号 の 代 入 2 :
3 : o p e n ( IN , "./ D a t a b a s e / T e s t d a t a . txt "); * 1 4 : l o c a l ( @ t e s t d a t a _ L i s t ) = < IN >;
5 : c l o s e ( IN );
6 :
7 : f o r e a c h $ c h e c k ( @ t e s t d a t a _ L i s t ) { * 2 8 : my @ c h e c k _ l i s t = s p l i t (/:/ , " $ c h e c k ");
9 : if ( @ c h e c k _ l i s t [2] eq $ q u e s t i o n _ n u m b e r ){
10: if ( @ c h e c k _ l i s t [0] ne " " ) { p u s h ( @ i n p u t _ b o x , @ c h e c k _ l i s t [ 0 ] ) ; } 11: p u s h ( @ o u t p u t _ b o x , @ c h e c k _ l i s t [ 1 ] ) ;
12: }
1 3 : } 14:
15: $n = $ # o u t p u t _ b o x + 1; * 3
図 4.3: テストデータ読み込みのソースコード
*1では4.2.3節に記載されている表4.4のテストーデータを管理するテーブルの情報を全て読み込み,
その内容を一旦配列変数(testdata List)に格納している.
*2ではテーブルの内容を保存した配列変数の中身を一つ一つ見ている. 9行目の場所で登録されたテス トデータの問題番号とユーザが選択した問題番号を比較して,同じ場合はテストデータとしてその内容を入 力データを格納する配列変数(input box)に,出力データを格納する配列変数(output box)にそれぞれ格納 していく.
*3ではテストデータの個数=比較回数でもあるので,比較回数を変数(n)に格納している.
第4章 実装 31 コンパイル
図4.4では,ユーザが記述したJavaファイルのコンパイルを行う. 最初に”javac”コマンドを用いてJava ファイルのコンパイルを行う. この時に表示されたエラー内容を一時的にファイルに保存しておく.
1 : l o c a l ( $sf ) = @_ ; 引 数 と し て 変 数( sf )に フ ァ イ ル 名 を 代 入 2 :
3 : s y s t e m (" j a v a c $sf 2 > $ r u n _ s t d e r r "); * 1 4 : o p e n ( IN ," < $ r u n _ s t d e r r ");
5 : @ r u n _ c h e c k = < IN >;
6 : c l o s e ( IN );
7 :
8 : if ( @ r u n _ c h e c k != "") { * 2
9 : p r i n t " - - - C O M P I L E E R R O R ( B E G I N ) - - -\ n ";
10: f o r e a c h $ w o r d ( @ r u n _ c h e c k ) { p r i n t " $ w o r d \ n ";}
11: p r i n t " - - - C O M P I L E E R R O R ( END ) - - -\ n ";
12: & c l e a n u p _ a n d _ e x i t ; 1 3 : }
図 4.4: コンパイル部のソースコード
*1では”javac”コマンドを用いてコンパイルしている. その時に出力されたエラー内容をファイルに保 存している. そのファイルの名前を持っている変数が(run stderr)であり, 最後にエラー内容を配列変数 (run check)に代入している.
*2ではエラー内容が格納されている配列変数(run check)が空かどうか判定している. もし空でない場 合はコンパイルエラーが発生しているので, 10行目の場所で配列変数の中身を全て表示している. 最後に12 行目の関数(cleanup and exit)に入ってシステムを終了させる.
第4章 実装 32 実行
「コンパイル」で生成されたファイルを実行する部分である. ”java”コマンドを用いて入力データを引数 に取り実行している. 「実行部」のソースを下の図4.5に示す.
1 : for ( $i = 0; $i < $n ; $i ++) { * 1 2 : if ( @ i n p u t _ b o x [ $i ] ne "") {
3 : o p e n ( INPUT , " < $td / @ i n p u t _ b o x [ $i ] " ) ; 4 : $ a r g u m e n t = < INPUT >;
5 : c l o s e ( I N P U T );
6 : }
7 : * 2
8 : s y s t e m ("( u l i m i t - f 1 - t 2; j a v a - cp $ w o r k d i r $ s o u r c e f i l e $ a r g u m e n t
9 : > $ r u n _ s t d o u t ) 2 > $ s h e l l _ s t d e r r ");
10: o p e n ( IN , " < $ s h e l l _ s t d e r r ");
11: l o c a l ( @ s h e l l _ c h e c k ) = < IN >;
12: c l o s e ( IN );
13:
14: if ( @ s h e l l _ c h e c k != "") { * 3
15: p r i n t " - - - RUN T I M E E R R O R ( B E G I N ) - - -\ n ";
16: f o r e a c h $ w o r d ( @ s h e l l _ c h e c k ) { p r i n t " $ w o r d \ n ";}
17: p r i n t " - - - RUN T I M E E R R O R ( END ) - - -\ n ";
18: & c l e a n u p _ a n d _ e x i t ;
19: }
20:
21: o p e n ( IN ," < $ r u n _ s t d o u t "); * 4 22: $ p r o g r a m _ o u t p u t = < IN >;
23: c l o s e ( IN );
24: o p e n ( OUTPUT , " < $td / @ o u t p u t _ b o x [ $i ] " ) ; 25: $ a n t i c i p a t i o n = < OUTPUT >;
26: c l o s e ( O U T P U T );
27: & c h e c k _ r e s u l t ( $ p r o g r a m _ o u t p u t , $ a r g u m e n t , $ a n t i c i p a t i o n );
2 8 : }
図 4.5: 実行部のソースコード
*1では比較回数である変数(n)を用いてその回数分実行を行っている. また2行目〜5行目では入力 データが格納されている配列変数(input box)から一つずつ入力データを変数(argument)に格納している.
*2では,まず8〜9行目では実行するファイル名が代入されている変数(sourcefile)を入力データが入っ ている変数(argument)を引数にして実行している. ”ulimit”の部分では実行時にファイル容量と実行時間 に制限を付けて実行させている. これにより無限ループが生じた場合に対処している. また出力結果を変数
(run stdout)に代入し,エラー内容を保存するファイル名を持っている変数(shell stderr)を使ってファイル
に保存している. 10行〜12行目ではエラー内容が保存されているファイルを呼び出して, その内容を配列 変数(shell check)に格納している.
第4章 実装 33
*3はコンパイル部の*2と同じなので説明は省く.
*4では実行時にエラーが無かったので, 最初に出力結果を変数(program output)に代入している. 次 にテストデータの出力データを変数(anticipation)に代入している. 最後に2つの出力結果を比較するため に,関数(check result)で比較を行う.
判定
図4.5の27行目にある関数(check result)で判定を行っている. ここでは引数として出力結果,入力デー タ,出力データが渡される. 出力結果と出力データを空白を無視出来るマッチングで比較し,同じ場合は最 後の比較回数に到達するまで, 実行部に戻り次ぎの入力データを引数にしての実行を行う. しかし,比較し た結果が違った場合は,その段階で「結果の出力」に移行するようになっている.
結果の出力
ここでは,テストデータと比較した結果をユーザに表示する部分である. 出力結果が全て予想出力と同じ 場合は,最後に比較した出力結果だけを表示するようになっている.
しかし,出力結果が一つでも違った場合はその時の入力データ及び出力結果を表示するようにしている.
第4章 実装 34