下準備
テストドライバの導入
第11回目分のテストドライバを導入する。以下の手順で行う。
- ダウンロード のページを開く (ここをクリック)
- プロジェクト「java20XX」にある「test」の左側の「+」をクリック
- ツリーが展開されるので「install-libraries.xml」を右クリック
- 「実行(R)」にマウスカーソルを合わせる
- 「1 Ant ビルド」をクリック
- 「コンソール」タブに"BUILD SUCCESSFUL"と表示されれば成功
- eclipseの画面でプロジェクト「java20XX」を右クリック
- メニューが表示されるので、「更新」あるいは「最新表示」をクリック
- "week11.zip" をデスクトップなどにダウンロード
- eclipseの画面でプロジェクト「java20XX」を右クリック
- メニューから「インポート(I)」を選択
- 「インポート」ウィンドウが表示されるので、「Zip ファイル」を選択
- 「次へ(N)」をクリック
- 宛先フォルダー(L): が「java20XX」になっていることを確認
- From zip file: の右側にある 「参照(R)...」あるいは「ブラウズ(R)...」をクリック
- ファイルダイアログが表示されるので、ダイアログ内に表示されたダウンロードしたファイルをダブルクリック
- 前の画面に戻るので、From zip file: のエリアに正しいパスが入力されていることを確認
- フォルダ「/」の左にチェックがついていることを確認 (ついていなければチェックボックスをクリック)
- 「警告を出さずに既存リソースを上書き」にチェックがついていることを確認 (上書きしたくないファイルがある場合はチェックを外す)
- 「終了 (F)」をクリック
第11週目テストドライバの導入に成功すると、java20XX プロジェクトの test フォルダに j1.lesson11.xml というフォルダが作成される。
パッケージの作成
過去の演習を参考にして、「j1.lesson11」というパッケージを作成する。
配列を使用したプログラム
出席カードからクラス毎の出席者数を計算するプログラムを作成する。
このプログラムでは、学籍番号の下4桁を格納した配列を作成し、その配列に入っているデータを元にそれぞれのクラスを判定し、クラス別に学生をカウントする。
擬似コードの作成
「プログラム全体」の擬似コード
プログラム全体の擬似コードは以下のようにする。とりあえずこの演習内では出席カード一覧の配列を以下のように設定することにする。
プログラム全体 出席カード一覧 = 次の要素を持つ配列 { 50, 1010, 1073, 1200, 1076, 1182, 1099, 1197, 1052, 53, 160, 51, 2100, 161, 1188, 56, 1180, 163, 0, 1000 } クラスごとの出席数 = クラスごとに出席をカウント(出席カード一覧) print "Aクラス: クラスごとの出席者数[0]人", 改行 print "Bクラス: クラスごとの出席者数[1]人", 改行 print "Cクラス: クラスごとの出席者数[2]人", 改行 print "Dクラス: クラスごとの出席者数[3]人", 改行 print "?クラス: クラスごとの出席者数[4]人", 改行
「クラスごとに出席をカウント」という手続きをあいまいにしているため、次の擬似コードで詳細化する。
「クラスごとに出席をカウント」の擬似コード
本学部では、学籍番号の下4桁のうち、上2桁によってクラスを判定できる。詳しくは第06週の演習を参照のこと。
クラスごとに出席をカウント(学籍番号一覧) クラスごとの出席者数 = 長さ5の新しい配列 クラスごとの出席者数の各要素を0に設定 for i を 0 から 学籍番号一覧の個数 - 1 switch 学籍番号一覧[i]の下2桁を削った値 0のとき クラスごとの出席者数[0] を1加算し、switch を抜ける 1のとき クラスごとの出席者数[1] を1加算し、switch を抜ける 10のとき クラスごとの出席者数[2] を1加算し、switch を抜ける 11のとき クラスごとの出席者数[3] を1加算し、switch を抜ける それ以外 クラスごとの出席者数[4] を1加算し、switch を抜ける クラスごとの出席者数を返す
この擬似コードでは、「クラスごとの出席者数」という配列を次のように使っている。
インデックス | 意味 |
---|---|
[0] | Aクラスの出席者数 |
[1] | Bクラスの出席者数 |
[2] | Cクラスの出席者数 |
[3] | Dクラスの出席者数 |
[4] | 不明なクラスの出席者数 |
骨格の作成
クラスの作成
以下の手順で、パッケージ「j1.lesson11」に「Attendance」クラスを作成する。
- 先ほど作成したパッケージ 「j1.lesson11」の上で右クリック
- マウスカーソルを「新規」に合わせる
- 「クラス」をクリック
- クラス名は Attendance とする
擬似コードの貼り付け (骨格のみ)
作成したクラスに、各擬似コードの名前を貼り付ける。
package j1.lesson11; public class Attendance { // プログラム全体 // クラスごとに出席をカウント(学籍番号一覧) }
mainメソッドの作成 (骨格のみ)
擬似コード「プログラム全体」に合わせて、クラス「Attendance」内にpublic static void main(String[] args) から始まるメソッドを作成する。
countByEachClass メソッドの作成
擬似コード「クラスごとに出席をカウント(学籍番号一覧)」に合わせて、クラス「Attendance」内に「countByEachClass」という名前を持ち、仮引数に int[] 型を取るメソッドを作成する。また、メソッドの戻り値は int[] とする。骨格だけでよいのでメソッドの中身は return new int[0]; とでも書いておく。
仮引数の名前は何でもよいが、ここではその意味を考えて attendanceCards としておく。
全体の骨格
ここまでのプログラムの骨格は以下のようになる。
package j1.lesson11; public class Attendance { // プログラム全体 public static void main(String[] args) { } // クラスごとに出席をカウント(学籍番号一覧) public static int[] countByEachClass(int[] attendanceCards) { return new int[0]; } }
骨格テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
「Attendance に対する骨格テスト」 を実行する。
骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
(クラス名), existence | j1.lesson11 に対象のクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値の型を間違えている (正しくは <T>) |
プログラムへの変換
countByEachClass メソッドの実装
メソッド countByEachClass の中身を記述する。
まずは擬似コードをコメントとして貼り付ける
// クラスごとに出席をカウント(学籍番号一覧) public static int[] countByEachClass(int[] attendanceCards) { // クラスごとの出席者数 = 長さ5の新しい配列 // クラスごとの出席者数の各要素を0に設定 // for i を 0 から 学籍番号一覧の個数 - 1 // switch 学籍番号一覧[i]の下2桁を削った値 // 0のとき // クラスごとの出席者数[0] を1加算し、switch を抜ける // 1のとき // クラスごとの出席者数[1] を1加算し、switch を抜ける // 10のとき // クラスごとの出席者数[2] を1加算し、switch を抜ける // 11のとき // クラスごとの出席者数[3] を1加算し、switch を抜ける // それ以外 // クラスごとの出席者数[4] を1加算し、switch を抜ける // クラスごとの出席者数を返す return new int[0]; }
これを元にプログラムを作成する。書き終わったら最後に入れていたダミーの return 文はいらないので削除する。
// クラスごとに出席をカウント(学籍番号一覧) public static int[] countByEachClass(int[] attendanceCards) { // クラスごとの出席者数 = 長さ5の新しい配列 int[] turnouts = new int[5]; // クラスごとの出席者数の各要素を0に設定 // for i を 0 から 学籍番号一覧の個数 - 1 for (int i = 0; i < attendanceCards.length; i++) { // switch 学籍番号一覧[i]の下2桁を削った値 switch (attendanceCards[i] / 100) { // 0のとき case 0: // クラスごとの出席者数[0] を1加算し、switch を抜ける turnouts[0]++; break; // 1のとき case 1: // クラスごとの出席者数[1] を1加算し、switch を抜ける turnouts[1]++; break; // 10のとき case 10: // クラスごとの出席者数[2] を1加算し、switch を抜ける turnouts[2]++; break; // 11のとき case 11: // クラスごとの出席者数[3] を1加算し、switch を抜ける turnouts[3]++; break; // それ以外 default: // クラスごとの出席者数[4] を1加算し、switch を抜ける turnouts[4]++; break; } } // クラスごとの出席者数を返す return turnouts; }
Javaでは、配列を作成した瞬間に各要素の値が 0 に設定されるため、「クラスごとの出席者数の各要素を0に設定」という擬似コードはプログラム上では無視される。
countByEachClass のテスト
余裕があれば、countByEachClass に対する単体テストを作成せよ。もし時間が無い場合は、次のようにmainメソッド内にテスト用のコードを書いて実行してみること。
public static void main(String[] args) { int[] testPattern = { 0, 100, 101, 1000, 1001, 1002, 1100, 1101, 1102, 1103, -1, 200, 1200, 2000, 9999 }; int[] testResult = countByEachClass(testPattern); System.out.println(testResult[0]); System.out.println(testResult[1]); System.out.println(testResult[2]); System.out.println(testResult[3]); System.out.println(testResult[4]); }
main メソッドの実装
続けて、先ほど作成した Attendance クラスの main メソッドの中身を記述する。
まずは擬似コードをコメントとして貼り付ける。
// プログラム全体 public static void main(String[] args) { // 出席カード一覧 = 次の要素を持つ配列 { // 50, 1010, 1073, 1200, 1076, // 1182, 1099, 1197, 1052, 53, // 160, 51, 2100, 161, 1188, // 56, 1180, 163, 0, 1000 } // クラスごとの出席数 = クラスごとに出席をカウント(出席カード一覧) // print "Aクラス: クラスごとの出席者数[0]人", 改行 // print "Bクラス: クラスごとの出席者数[1]人", 改行 // print "Cクラス: クラスごとの出席者数[2]人", 改行 // print "Dクラス: クラスごとの出席者数[3]人", 改行 // print "?クラス: クラスごとの出席者数[4]人", 改行 }
これを元にプログラムを作成する。
// プログラム全体 public static void main(String[] args) { // 出席カード一覧 = 次の要素を持つ配列 { // 50, 1010, 1073, 1200, 1076, // 1182, 1099, 1197, 1052, 53, // 160, 51, 2100, 161, 1188, // 56, 1180, 163, 0, 1000 } int[] attendanceCards = { 50, 1010, 1073, 1200, 1076, 1182, 1099, 1197, 1052, 53, 160, 51, 2100, 161, 1188, 56, 1180, 163, 0, 1000 }; // クラスごとの出席数 = クラスごとに出席をカウント(出席カード一覧) int[] turnouts = countByEachClass(attendanceCards); // print "Aクラス: クラスごとの出席者数[0]人", 改行 System.out.println("Aクラス" + turnouts[0] + "人"); // print "Bクラス: クラスごとの出席者数[1]人", 改行 System.out.println("Bクラス" + turnouts[1] + "人"); // print "Cクラス: クラスごとの出席者数[2]人", 改行 System.out.println("Cクラス" + turnouts[2] + "人"); // print "Dクラス: クラスごとの出席者数[3]人", 改行 System.out.println("Dクラス" + turnouts[3] + "人"); // print "?クラス: クラスごとの出席者数[4]人", 改行 System.out.println("?クラス" + turnouts[4] + "人"); }
全体的には下記のようなプログラムにする。
package j1.lesson11; public class Attendance { // プログラム全体 public static void main(String[] args) { // 出席カード一覧 = 次の要素を持つ配列 { // 50, 1010, 1073, 1200, 1076, // 1182, 1099, 1197, 1052, 53, // 160, 51, 2100, 161, 1188, // 56, 1180, 163, 0, 1000 } int[] attendanceCards = { 50, 1010, 1073, 1200, 1076, 1182, 1099, 1197, 1052, 53, 160, 51, 2100, 161, 1188, 56, 1180, 163, 0, 1000 }; // クラスごとの出席数 = クラスごとに出席をカウント(出席カード一覧) int[] turnouts = countByEachClass(attendanceCards); // print "Aクラス: クラスごとの出席者数[0]人", 改行 System.out.println("Aクラス" + turnouts[0] + "人"); // print "Bクラス: クラスごとの出席者数[1]人", 改行 System.out.println("Bクラス" + turnouts[1] + "人"); // print "Cクラス: クラスごとの出席者数[2]人", 改行 System.out.println("Cクラス" + turnouts[2] + "人"); // print "Dクラス: クラスごとの出席者数[3]人", 改行 System.out.println("Dクラス" + turnouts[3] + "人"); // print "?クラス: クラスごとの出席者数[4]人", 改行 System.out.println("?クラス" + turnouts[4] + "人"); } // クラスごとに出席をカウント(学籍番号一覧) public static int[] countByEachClass(int[] attendanceCards) { // クラスごとの出席者数 = 長さ5の新しい配列 int[] turnouts = new int[5]; // クラスごとの出席者数の各要素を0に設定 // for i を 0 から 学籍番号一覧の個数 - 1 for (int i = 0; i < attendanceCards.length; i++) { // switch 学籍番号一覧[i]の下2桁を削った値 switch (attendanceCards[i] / 100) { // 0のとき case 0: // クラスごとの出席者数[0] を1加算し、switch を抜ける turnouts[0]++; break; // 1のとき case 1: // クラスごとの出席者数[1] を1加算し、switch を抜ける turnouts[1]++; break; // 10のとき case 10: // クラスごとの出席者数[2] を1加算し、switch を抜ける turnouts[2]++; break; // 11のとき case 11: // クラスごとの出席者数[3] を1加算し、switch を抜ける turnouts[3]++; break; // それ以外 default: // クラスごとの出席者数[4] を1加算し、switch を抜ける turnouts[4]++; break; } } // クラスごとの出席者数を返す return turnouts; } }
プログラムの実行
先週までと同じ手順でプログラムを実行する。
実行が成功すると、以下のように表示される。
Aクラス5人 Bクラス3人 Cクラス6人 Dクラス4人 ?クラス2人
機能テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
「Attendance に対する機能テスト」 を実行する。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
期待された結果と異なります | 出力された結果が期待された値と異なる。 |
機能テストの項目
項目名 | テストの内容 |
---|---|
testRun | プログラムを実行 |
配列の逆順コピー
配列を逆順に並び替えつつコピーするプログラムを考える。
擬似コードの作成
「プログラム全体」の擬似コード
プログラム全体の擬似コードは以下のようにする。
プログラム全体 array = 5個の要素を持つ新しい配列 for i を 0 から 配列 a の長さ - 1 print "値を入力:" array[i] = コンソール入力 (整数) reversed = reverse(array) print array print 改行 print reversed
この擬似コードでは、reverse という処理を行っているため、次の擬似コードで詳細化する。
「reverse」の擬似コード
配列を逆順にコピーする場合、次のようにすればよい。
- コピー元の配列を末尾から参照していく
- コピー先の配列に先頭から代入していく
イメージとしては次のような流れになる。
これを元に、「プログラム全体」で使用されている、reverse の擬似コードは以下のようにする。
reverse(a) newA = a と同じ長さの新しい配列 for i を 0 から newAの長さ - 1 まで newA[i] = a[newAの長さ - 1 - i] newA を返す
配列の各要素を表示する擬似コード
プログラム全体の擬似コードの中に、
print array
というものがあった。しかし Javaでは System.out.print() メソッドなどを使用して配列を表示しようとすると、次のようになってしまう。
[I@119c082
ここに表示されているものは、いわゆる配列のID (のようなもの) で、配列の各要素については表示してくれない。
そこで、配列を表示するためのプログラムを作成する必要がある。
print-array(a) for i を 0 から a の長さ - 1 print a[i] print " "
骨格の作成
クラスの作成
以下の手順で、パッケージ「j1.lesson11」に「ArrayReverse」クラスを作成する。
- 先ほど作成したパッケージ 「j1.lesson11」の上で右クリック
- マウスカーソルを「新規」に合わせる
- 「クラス」をクリック
- クラス名は ArrayReverse とする
擬似コードの貼り付け (骨格のみ)
作成したクラスに、各擬似コードの名前を貼り付ける。ただし、「プログラム全体」ではコンソールからの入力を行っていたため、import java.io.*; も追加する。
package j1.lesson11; import java.io.*; public class ArrayReverse { // プログラム全体 // reverse(a) // print-array(a) }
mainメソッドの作成 (骨格のみ)
擬似コード「プログラム全体」に合わせて、クラス「ArrayReverse」内にpublic static void main(String[] args) から始まるメソッドを作成する。ただし、擬似コード「プログラム全体」ではコンソールからの入力を行っていたので、throws IOExceptionをつける。
reverse メソッドの作成 (骨格のみ)
擬似コード「reverse」に合わせて、クラス「ArrayReverse」内に「reverse」という名前を持ち、仮引数に int[] 型を取るメソッドを作成する。また、メソッドの戻り値は逆順に並び替えた配列を返すため int[] とする。骨格だけでよいのでメソッドの中身は return new int[0]; とでも書いておく。
仮引数の名前は何でもよいが、ここでは擬似コードに合わせて a としておく。
printArray メソッドの作成 (骨格のみ)
擬似コード「print-array」に合わせて、クラス「ArrayReverse」内に「printArray」という名前を持ち、仮引数に int[] 型を取るメソッドを作成する。
仮引数の名前は何でもよいが、ここでは擬似コードに合わせて a としておく。
全体の骨格
ここまでのプログラムの骨格は以下のようになる。
package j1.lesson11; import java.io.*; public class ArrayReverse { // プログラム全体 public static void main(String[] args) throws IOException { } // reverse(a) public static int[] reverse(int[] a) { return new int[0]; } // print-array(a) public static void printArray(int[] a) { } }
骨格テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
「ArrayReverse に対する骨格テスト」 を実行する。
骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
(クラス名), existence | j1.lesson11 に対象のクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値の型を間違えている (正しくは <T>) |
(メソッド名), throws java.io.IOException | メソッドを作る際に throws IOException が抜けている |
プログラムへの変換
reverse メソッドの実装
メソッド reverse の中身を記述する。
まずは擬似コードをコメントとして貼り付ける
// reverse(a) public static int[] reverse(int[] a) { // newA = a と同じ長さの新しい配列 // for i を 0 から newAの長さ - 1 まで // newA[i] = a[newAの長さ - 1 - i] // newA を返す return new int[0]; }
これを元にプログラムを作成する。書き終わったら最後に入れていたダミーの return 文はいらないので削除する。
// reverse(a) public static int[] reverse(int[] a) { // newA = a と同じ長さの新しい配列 int[] newA = new int[a.length]; // for i を 0 から newAの長さ - 1 まで for (int i = 0; i < newA.length; i++) { // newA[i] = a[newAの長さ - 1 - i] newA[i] = a[newA.length - 1 - i]; } // newA を返す return newA; }
printArray メソッドの実装
メソッド printArray の中身を記述する。
まずは擬似コードをコメントとして貼り付ける
// print-array(a) public static void printArray(int[] a) { // for i を 0 から a の長さ - 1 // print a[i] // print " " }
これを元にプログラムを作成する。
// print-array(a) public static void printArray(int[] a) { // for i を 0 から a の長さ - 1 for (int i = 0; i < a.length; i++) { // print a[i] System.out.print(a[i]); // print " " System.out.print(" "); } }
reverse, printArray メソッドのテスト
余裕があれば、reverse, printArray に対する単体テストを作成せよ。もし時間が無い場合は、次のようにmainメソッド内にテスト用のコードを書いて実行してみること。
public static void main(String[] args) { int[] testPattern = {1, 2, 3}; int[] testResult = reverse(testResult); printArray(testPattern); System.out.println(""); printArray(testResult); }
main メソッドの実装
続けて、先ほど作成した ArrayReverse クラスの main メソッドの中身を記述する。
まずは擬似コードをコメントとして貼り付ける。
// プログラム全体 public static void main(String[] args) throws IOException { // array = 5個の要素を持つ新しい配列 // for i を 0 から 配列 a の長さ - 1 // print "値を入力:" // array[i] = コンソール入力 (整数) // reversed = reverse(array) // print array // print 改行 // print reversed }
これを元にプログラムを作成する。
// プログラム全体 public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); // array = 5個の要素を持つ新しい配列 int[] array = new int[5]; // for i を 0 から 配列 a の長さ - 1 for (int i = 0; i < array.length; i++) { // print "値を入力:" System.out.print("値を入力:"); // array[i] = コンソール入力 (整数) array[i] = Integer.parseInt(reader.readLine()); } // reversed = reverse(array) int[] reversed = reverse(array); // print array printArray(array); // print 改行 System.out.println(""); // print reversed printArray(reversed); }
上記のプログラム内にある「print 改行」を表すJavaの命令
System.out.println("");
は、以下の命令が同じ意味になる。
System.out.println();
このように、引数に何も取らない場合は改行のみを表示する。つまり、空の文字列 ("") を表示した後に改行と同じになる。
全体的には下記のようなプログラムにする。
package j1.lesson11; import java.io.*; public class ArrayReverse { // プログラム全体 public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); // array = 5個の要素を持つ新しい配列 int[] array = new int[5]; // for i を 0 から 配列 a の長さ - 1 for (int i = 0; i < array.length; i++) { // print "値を入力:" System.out.print("値を入力:"); // array[i] = コンソール入力 (整数) array[i] = Integer.parseInt(reader.readLine()); } // reversed = reverse(array) int[] reversed = reverse(array); // print array printArray(array); // print 改行 System.out.println(""); // print reversed printArray(reversed); } // reverse(a) public static int[] reverse(int[] a) { // newA = a と同じ長さの新しい配列 int[] newA = new int[a.length]; // for i を 0 から newAの長さ - 1 まで for (int i = 0; i < newA.length; i++) { // newA[i] = a[newAの長さ - 1 - i] newA[i] = a[newA.length - 1 - i]; } // newA を返す return newA; } // print-array(a) public static void printArray(int[] a) { // for i を 0 から a の長さ - 1 for (int i = 0; i < a.length; i++) { // print a[i] System.out.print(a[i]); // print " " System.out.print(" "); } } }
プログラムの実行
先週までと同じ手順でプログラムを実行する。
入力に 1, 2, 3, 4, 5 と順に与えてやると、以下のように表示される。
値を入力:1 値を入力:2 値を入力:3 値を入力:4 値を入力:5 1 2 3 4 5 5 4 3 2 1
機能テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
「ArrayReverse に対する機能テスト」 を実行する。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
期待された結果と異なります | 出力された結果が期待された値と異なる。 |
機能テストの項目
項目名 | テストの内容 |
---|---|
testRun | 入力に 1, 2, 3, 4, 5 を与えてプログラムを実行 |