第09週目演習

下準備

テストドライバの導入

第09回目分のテストドライバを導入する。以下の手順で行う。

  1. ダウンロード のページを開く (ここをクリック)
  2. プロジェクト「java20XX」にある「test」の左側の「+」をクリック
  3. ツリーが展開されるので「install-libraries.xml」を右クリック
  4. 「実行(R)」にマウスカーソルを合わせる
  5. 「1 Ant ビルド」をクリック
  6. 「コンソール」タブに"BUILD SUCCESSFUL"と表示されれば成功
  7. eclipseの画面でプロジェクト「java20XX」を右クリック
  8. メニューが表示されるので、「更新」あるいは「最新表示」をクリック
  9. "week09.zip" をデスクトップなどにダウンロード
  10. eclipseの画面でプロジェクト「java20XX」を右クリック
  11. メニューから「インポート(I)」を選択
  12. 「インポート」ウィンドウが表示されるので、「Zip ファイル」を選択
  13. 「次へ(N)」をクリック
  14. 宛先フォルダー(L): が「java20XX」になっていることを確認
  15. From zip file: の右側にある 「参照(R)...」あるいは「ブラウズ(R)...」をクリック
  16. ファイルダイアログが表示されるので、ダイアログ内に表示されたダウンロードしたファイルをダブルクリック
  17. 前の画面に戻るので、From zip file: のエリアに正しいパスが入力されていることを確認
  18. フォルダ「/」の左にチェックがついていることを確認 (ついていなければチェックボックスをクリック)
  19. 「警告を出さずに既存リソースを上書き」にチェックがついていることを確認 (上書きしたくないファイルがある場合はチェックを外す)
  20. 「終了 (F)」をクリック

第09週目テストドライバの導入に成功すると、java20XX プロジェクトの test フォルダに j1.lesson09.xml というファイルが作成される。

パッケージの作成

過去の演習を参考にして、「j1.lesson09」というパッケージを作成する。

演習の前に

先週と同様に、main以外のメソッドについても骨格テストが行われる。
骨格テストは、「クラスとその中に必要な全てのメソッドを宣言した後」に実行する。

また、それぞれのメソッドが単独で呼び出された際に、正しい動作を行ってくれるかどうかを調べる単体テストも実行する。このテストによって、どのメソッド内で問題が発生したかなど、プログラムの誤りをより特定しやすくなる。

簡単な再帰メソッド

簡単な再帰メソッドとして、講義中にも紹介のあった 階乗を計算する再帰メソッド を作成する。

演習

クラスの作成

以下の手順で、パッケージ「j1.lesson09」に「Factorial」クラスを作成する。

  1. 先ほど作成したパッケージ 「j1.lesson09」の上で右クリック
  2. マウスカーソルを「新規」に合わせる
  3. 「クラス」をクリック
  4. クラス名は Factorial とする
  5. 先週までと同じ手順でクラスを作成する

mainメソッドの作成 (骨格)

クラス「Factorial」内にpublic static void main(String[] args) から始まるメソッドを作成する。

factorial メソッドの作成 (骨格)

クラス「Factorial」内に 「factorial」という名前を持ち、仮引数に1つのint型の変数をとるメソッドを作成する。また、メソッドの戻り値はintとする。骨格だけでよいのでメソッドの中身は空でよい。

仮引数の名前は何でも良いが、ここでは n という名前をつけることにする。

全体の骨格

ここまでのプログラムの骨格は以下のようになる。

package j1.lesson09;

public class Factorial {

    public static void main(String[] args) {
    }
    
    public static int factorial(int n) {
        return 0;
    }
}

main メソッドのほかに、factorial メソッドを作成している。その際、戻り値が void 型でないメソッドは、return文を入れないとコンパイルエラーになるため、ダミーの戻り値を用意している。

骨格テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「Factorial に対する骨格テスト」 を実行する。

骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。

メッセージ 詳細
(クラス名), existence j1.lesson09 に Factorial クラスが存在していない。パッケージやクラス名を確認
(メソッド名), existence 指定されたメソッドが存在しない
(メソッド名), public メソッドを作る際に public が抜けている
(メソッド名), static メソッドを作る際に static が抜けている
(メソッド名), type <T> メソッドを作る際に戻り値の型を間違えている (正しくは <T>)

メソッドの実装 (factorial)

メソッド factorial を実装する。このメソッドは、実行されると、引数に指定された値 n の階乗を返す。

public static int factorial(int n) {
    // factorial(n) = 1  (n == 0)
    if (n == 0) {
        return 1;
    }
    
    // factorial(n) = n * factorial(n-1)  (n != 0)
    else {
        return n * factorial(n - 1);
    }
}

単体テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

mainメソッド内に各メソッドを呼び出すプログラムを書いてみる。例えば以下の様な形。

public static void main(String[] args) {
    System.out.println(factorial(0));
    System.out.println(factorial(1));
    System.out.println(factorial(2));
    System.out.println(factorial(5));
}

このプログラムを実行し、各メソッドが期待通りに動いていることを確認する。確認したら、mainメソッドの中身は空にして良い。

「Factorial に対する単体テスト」 を実行する。

単体テストを行った際に緑のバーが表示されれば、各メソッドはある程度正しく作成されていると思われる。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。

メッセージ 詳細
(メソッド名), 期待された結果と異なります (メソッド名)を起動した結果が期待された結果と異なる。下記のテスト項目を参照

テスト項目は以下の通りである。

項目名 詳細
testFactorial_0 factorial(int) を実引数 (0) で起動
testFactorial_1 factorial(int) を実引数 (1) で起動
testFactorial_2 factorial(int) を実引数 (2) で起動
testFactorial_3 factorial(int) を実引数 (3) で起動
testFactorial_10 factorial(int) を実引数 (10) で起動
testFactorial_13 factorial(int) を実引数 (13) で起動

main メソッドの実装

続けて、先ほど作成した Factorial クラスの main メソッドの中身を記述する。mainメソッドでは、factorial を様々な実引数を伴って呼び出し、戻り値を表示する。

全体的には下記のようなプログラムにする。ただし、一文書くごとにCtrl+Sで保存とコンパイルを行い、常に文法エラーが発生していないことを確認すること。

package j1.lesson09;

public class Factorial {

    public static void main(String[] args) {
        for (int i = 0; i <= 10; i++) {
            System.out.println("factorial(" + i + ") = " + factorial(i));
        }
    }
    
    public static int factorial(int n) {
        // factorial(n) = 1  (n == 0)
        if (n == 0) {
            return 1;
        }
        
        // factorial(n) = n * factorial(n-1)  (n != 0)
        else {
            return n * factorial(n - 1);
        }
    }
}

プログラムの実行

先週までと同じ手順でプログラムを実行する。

実行が成功すると、以下のように表示される。

factorial(0) = 1
factorial(1) = 1
factorial(2) = 2
factorial(3) = 6
factorial(4) = 24
factorial(5) = 120
factorial(6) = 720
factorial(7) = 5040
factorial(8) = 40320
factorial(9) = 362880
factorial(10) = 3628800

機能テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「Factorial に対する機能テスト」 を実行する。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。

メッセージ 詳細
期待された結果と異なります 出力された結果が期待された値と異なる。

機能テストの項目

項目名 テストの内容
testRun プログラムを実行

解説

解説のために、プログラムに行番号をつけて解説する。

01: package j1.lesson09;
02: 
03: public class Factorial {
04: 
05:     public static void main(String[] args) {
06:         for (int i = 0; i <= 10; i++) {
07:             System.out.println("factorial(" + i + ") = " + factorial(i));
08:         }
09:     }
10:     
11:     public static int factorial(int n) {
12:         // factorial(n) = 1  (n == 0)
13:         if (n == 0) {
14:             return 1;
15:         }
16:         
17:         // factorial(n) = n * factorial(n-1)  (n != 0)
18:         else {
19:             return n * factorial(n - 1);
20:         }
21:     }
22: }

少し複雑な再帰メソッド

再帰メソッドの例としてよく取り上げられる フィボナッチ数列 を計算するメソッドを作成する。また、その計算を行うためにメソッドを起動した回数を保存しておく。

フィボナッチ数列とは次のような数列である。

現実のモデルで表現したとすると、以下のようになる。

「ある生物は朝生まれて、二日後の朝に子孫を一匹だけ残し、それ以降は毎朝一匹ずつ子孫を増やしていく。1日目の朝に生まれたてのその生物を一匹だけ貰ってきた。n日目の朝にはその生物は何匹になっているか」

つまり、フィボナッチ数列の第 k 項を計算するメソッド fibonacci(int) は、以下のように書き表せる。

fibonacci(k) = 0  (k = 0)
fibonacci(k) = 1  (k = 1)
fibonacci(k) = fibonacci(k-1) + fibonacci(k-2)  (k >= 2)

演習

クラスの作成

以下の手順で、パッケージ「j1.lesson09」に「Fibonacci」クラスを作成する。

  1. 先ほど作成したパッケージ 「j1.lesson09」の上で右クリック
  2. マウスカーソルを「新規」に合わせる
  3. 「クラス」をクリック
  4. クラス名は Fibonacci とする
  5. 先週までと同じ手順でクラスを作成する

count フィールドの作成

クラス「Fibonacci」内に int 型のクラスフィールド count を作成する。

main メソッドの作成

クラス「Fibonacci」内にpublic static void main(String[] args) から始まるメソッドを作成する。

fibonacci メソッドの作成

クラス「Fibonacci」内に「fibonacci」という名前を持ち、仮引数に1つのint型の変数をとるメソッドを作成する。また、メソッドの戻り値はintとする。骨格だけでよいのでメソッドの中身は空でよい。

仮引数の名前は何でも良いが、ここでは k という名前をつけることにする。

全体の骨格

ここまでのプログラムの骨格は以下のようになる。どんなものを作成するかイメージを与えるため、コメントをつけている。

package j1.lesson09;

public class Fibonacci {
    
    // メソッドを起動した回数を記憶する
    static int count;

    // プログラムエントリ
    public static void main(String[] args) {
    }
    
    // フィボナッチ数列の 第 k 項を計算するメソッド
    public static int fibonacci(int k) {
        return 0;
    }
}

main メソッドのほかに、fibonacci メソッドを作成している。先ほどと同様に、戻り値の型が void でないメソッドにはダミーの値を返す return 文を挿入している。

骨格テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「Fibonacci に対する骨格テスト」 を実行する。

骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。

メッセージ 詳細
(クラス名), existence j1.lesson09 に Fibonacci クラスが存在していない。パッケージやクラス名を確認
(メソッド名), existence 指定されたメソッドが存在しない
(メソッド名), public メソッドを作る際に public が抜けている
(メソッド名), static メソッドを作る際に static が抜けている
(メソッド名), type <T> メソッドを作る際に戻り値の型を間違えている

メソッドの実装 (fibonacci)

メソッド fibonacci を実装する。このメソッドは、与えられた 1 つの整数 k に対して、フィボナッチ数列の第 k 項の値を返すメソッドである。

fibonacci(k) = 0  (k = 0)
fibonacci(k) = 1  (k = 1)
fibonacci(k) = fibonacci(k-1) + fibonacci(k-2)  (k >= 2)
public static int fibonacci(int k) {
    // 起動回数を 1 加算
    count++;
    
    // fibonacci(0) = 0
    if (k == 0) {
        return 0;
    }
    // fibonacci(1) = 1
    else if (k == 1) {
        return 1;
    }
    // fibonacci(k) = fibonacci(k-1) + fibonacci(k-2) (k > 2)
    else {
        return fibonacci(k - 1) + fibonacci(k - 2);
    }
}

単体テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

mainメソッド内に各メソッドを呼び出すプログラムを書いてみる。例えば以下の様な形。

public static void main(String[] args) {
  System.out.println(fibonacci(0));
  System.out.println(fibonacci(1));
  System.out.println(fibonacci(2));
  System.out.println(fibonacci(3));
  System.out.println(fibonacci(4));
}

このプログラムを実行し、各メソッドが期待通りに動いていることを確認する。確認したら、mainメソッドの中身は空にして良い。

「Fibonacci に対する単体テスト」 を実行する。

単体テストを行った際に緑のバーが表示されれば、各メソッドはある程度正しく作成されていると思われる。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。

メッセージ 詳細
(メソッド名), 期待された結果と異なります (メソッド名)を起動した結果が期待された結果と異なる。下記のテスト項目を参照

テスト項目は以下の通りである。

項目名 詳細
testFibonacci_0 fibonacci(int) を 実引数 (0) で起動
testFibonacci_1 fibonacci(int) を 実引数 (1) で起動
testFibonacci_2 fibonacci(int) を 実引数 (2) で起動
testFibonacci_3 fibonacci(int) を 実引数 (3) で起動
testFibonacci_10 fibonacci(int) を 実引数 (10) で起動

プログラムの作成

続けて、先ほど作成したFibonacciクラスのmainメソッドの中身を記述する。main メソッドでは、fibonacci を様々な実引数を伴って呼び出し、戻り値と毎回の計算に掛かったメソッド起動回数を表示する。

全体的には下記のようなプログラムにする。ただし、一文書くごとにCtrl+Sで保存とコンパイルを行い、常に文法エラーが発生していないことを確認すること。

package j1.lesson09;

public class Fibonacci {
    
    // メソッドを起動した回数を記憶する
    static int count;

    // プログラムエントリ
    public static void main(String[] args) {
        for (int i = 0; i <= 10; i++) {
            // 起動回数は毎回初期化する
            count = 0;
            System.out.println("fibonacci(" + i + ") = " + fibonacci(i));
            System.out.println("起動回数は" + count + "回");
        }
    }
    
    // フィボナッチ数列の 第 k 項を計算するメソッド
    public static int fibonacci(int k) {
        // 起動回数を 1 加算
        count++;
        
        // fibonacci(0) = 0
        if (k == 0) {
            return 0;
        }
        // fibonacci(1) = 1
        else if (k == 1) {
            return 1;
        }
        // fibonacci(k) = fibonacci(k-1) + fibonacci(k-2) (k >= 2)
        else {
            return fibonacci(k - 1) + fibonacci(k - 2);
        }
    }
}

プログラムの実行

先週までと同じ手順でプログラムを実行する。

実行が成功すると、以下のように表示される。

fibonacci(0) = 0
起動回数は1回
fibonacci(1) = 1
起動回数は1回
fibonacci(2) = 1
起動回数は3回
fibonacci(3) = 2
起動回数は5回
fibonacci(4) = 3
起動回数は9回
fibonacci(5) = 5
起動回数は15回
fibonacci(6) = 8
起動回数は25回
fibonacci(7) = 13
起動回数は41回
fibonacci(8) = 21
起動回数は67回
fibonacci(9) = 34
起動回数は109回
fibonacci(10) = 55
起動回数は177回

機能テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「Fibonacci に対する機能テスト」 を実行する。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「実行」ボタンをクリックする。

メッセージ 詳細
無限ループの可能性 繰り返し部分が無限に繰り返されていないか確認
期待された結果と異なります 出力された結果が期待された値と異なる。「期待された値」と「実際の値」を比較 (エラーメッセージが出力されているエリアの下のほうにあるバーをスクロールさせれば見ることができる) し確認。他にも直接プログラムを実行して結果を調べたり、エラーメッセージの2行目にあるat以下を参考にプログラムを見直す

機能テスト失敗時のヒント

メッセージ 詳細
期待された結果と異なります 出力された結果が期待された値と異なる。

機能テストの項目

項目名 テストの内容
testRun プログラムを実行

解説

解説のために、プログラムに行番号をつけて解説する。

01: package j1.lesson09;
02: 
03: public class Fibonacci {
04:     
05:     // メソッドを起動した回数を記憶する
06:     static int count;
07: 
08:     // プログラムエントリ
09:     public static void main(String[] args) {
10:         for (int i = 0; i <= 10; i++) {
11:             // 起動回数は毎回初期化する
12:             count = 0;
13:             System.out.println("fibonacci(" + i + ") = " + fibonacci(i));
14:             System.out.println("起動回数は" + count + "回");
15:         }
16:     }
17:     
18:     // フィボナッチ数列の 第 k 項を計算するメソッド
19:     public static int fibonacci(int k) {
20:         // 起動回数を 1 加算
21:         count++;
22:         
23:         // fibonacci(0) = 0
24:         if (k == 0) {
25:             return 0;
26:         }
27:         // fibonacci(1) = 1
28:         else if (k == 1) {
29:             return 1;
30:         }
31:         // fibonacci(k) = fibonacci(k-1) + fibonacci(k-2) (k >= 2)
32:         else {
33:             return fibonacci(k - 1) + fibonacci(k - 2);
34:         }
35:     }
36: }