繰り返し

今回の内容

今回はコンピューターに「繰り返し」を行わせるようなプログラムについて紹介します。繰り返しといっても全く同じ処理を何度も行うのではなく、毎回の処理ごとに少しずつ異なる操作も行えます。たとえば配列の要素を順番に処理する、などの処理には繰り返しが便利です。

この「繰り返し」はプログラムの基本的な構造である「順次」「分岐」「反復」のうちの、最後の1つである「反復」に該当します。これまでは「入力した回数だけダイアログを表示」など、処理を行う回数そのものが変化するようなプログラムは簡単に書けません でした。今回紹介する繰り返しを使えば、そのようなプログラムも簡単に書けるようになります。

図: 構造化定理(3/構造化定理の再掲)

今回の作業

今回の例題、練習問題、課題は以下のパッケージに作成してください。

パッケージの名前
j1.lesson07

クラスの作成方法については、「クラスを作成する」を参照してください。

処理の繰り返し

同一処理の繰り返し

まずは、簡単な繰り返しを行うプログラムを見てみましょう。

リスト: Repeat(3回)
package j1.lesson07;

import javax.swing.JOptionPane;

public class Repeat {

    public static void main(String[] args) {
        new Repeat().start();
    }

    void start() {
        for (int i = 0; i < 3; i++) {
            JOptionPane.showMessageDialog(null, "こんにちは!");
        }
    }
}

このプログラムには、forから始まる命令が書かれています。その中にあるブロックは「こんにちは!」とダイアログに表示するだけの命令ですが、実行するとダイアログは3回表示されます。

リスト: 3回の繰り返し
for (int i = 0; i < 3; i++) {
    JOptionPane.showMessageDialog(null, "こんにちは!");
}

このforから始まる一連の命令を、Javaではfor文 と呼びます。このfor文は、「int i = 0」「i < 3」「i++」と ; (セミコロン)で区切られた3つの部分と、ifやメソッドと同様に { … } (ブロック)から成ります。このブロックには繰り返し実行する命令を書き、最初の3つの部分には繰り返し方を書きます。

3つの部分のうち、1つ目の「int i = 0」はこれまでの変数宣言と同様で、整数の変数iを宣言しています。ここで宣言した変数はfor文全体で使え、変数の名前は自由に決められます。次の「i < 3」は繰り返すための条件で、この条件が成立している間はブロック内の命令を処理し続けます。そして、ブロック内の命令を1回処理するたびに、3つ目の「i++」が処理されます。このi++は「i = i + 1」と同じ効果で、最初に宣言した変数iに記憶させた値を1だけ増加させます。

図 : for文の構造

つまり、 for文が処理される際には、まず変数iに0を記憶させて、ブロックを処理するごとにiを1ずつ増やしていきます。このiが3より小さい場合(i < 3)にはブロックの処理を繰り返し、そうでない場合には繰り返しを終了させます。そのため、iは0, 1, 2と変化しながらブロックを合計3回処理し、iが3になったらブロックを処理せずに終了します。

少し書き換えて、「こんにちは!」と5回表示させてみましょう。

リスト: Repeat(5回)
package j1.lesson07;

import javax.swing.JOptionPane;

public class Repeat {

    public static void main(String[] args) {
        new Repeat().start();
    }

    void start() {
        for (int i = 0; i < 5; i++) {
            JOptionPane.showMessageDialog(null, "こんにちは!");
        }
    }
}

少し大きな回数を指定することもできます。

リスト: Repeat(100回)
package j1.lesson07;

import javax.swing.JOptionPane;

public class Repeat {

    public static void main(String[] args) {
        new Repeat().start();
    }

    void start() {
        for (int i = 0; i < 100; i++) {
            JOptionPane.showMessageDialog(null, "こんにちは!");
        }
    }
}

今回の繰り返し処理を使うと、なかなか処理が終了しないプログラムを簡単に書けてしまいます。そのようなプログラムを実行してしまった場合は、「プログラムを強制的に停止する」を参考にプログラムを強制的に終了させましょう。

続いて、この繰り返す回数を入力で自由に変更できるようにしてみましょう。

リスト: RepeatSpecified
package j1.lesson07;

import javax.swing.JOptionPane;

public class RepeatSpecified {

    public static void main(String[] args) {
        new RepeatSpecified().start();
    }

    void start() {
        int count = getCount();
        for (int i = 0; i < count; i++) {
            JOptionPane.showMessageDialog(null, "こんにちは!");
        }
    }

    int getCount() {
        String input = JOptionPane.showInputDialog("繰り返し回数を入力");
        int count = Integer.parseInt(input);
        return count;
    }
}

繰り返し条件を変更するだけで繰り返す回数も変わりましたが、今回はそこに変数を指定しています。

リスト: 指定された回数の繰り返し
int count = getCount();
for (int i = 0; i < count; i++) {
    JOptionPane.showMessageDialog(null, "こんにちは!");
}

getCountメソッドは入力された値を返すメソッドですので、変数countに記憶させた値は実行するたびに変わります。この値をfor文の繰り返し条件に使用すれば、繰り返し回数は入力された値によって決まることになります。

まとめると、for文は一般的に 次のような形で書きます。

for (<変数の宣言>; <繰り返し条件>; <変数の更新>) {
    <命令>
}

繰り返し条件に < (より小さい)以外を指定したり、変数の更新に ++ (1増加)以外を指定したりすることもできます。しかし、多くの場合は0から始まり100未満まで1ずつ、のような形式だけで十分であるので、この基本的なパターン をまず押さえておくのがよいでしょう。

繰り返し変数

次に、for文で宣言した変数を使ってみましょう。

リスト: ShowCount
package j1.lesson07;

import javax.swing.JOptionPane;

public class ShowCount {

    public static void main(String[] args) {
        new ShowCount().start();
    }

    void start() {
        for (int i = 0; i < 5; i++) {
            JOptionPane.showMessageDialog(null, i + "回目の表示です");
        }
    }
}

これを実行すると、「0回目の表示です」「1回目の表示です」…「4回目の表示です」といった具合に、繰り返しのたびに別の内容が表示されていきます。これは、for文で宣言した変数を、ブロックの中で利用しているためです。

リスト: 繰り返し回数の表示
for (int i = 0; i < 5; i++) {
    JOptionPane.showMessageDialog(null, i + "回目の表示です");
}

ブロックの中で利用している変数iは、for文の繰り返しかたを制御しています。最初の値は0ですが、繰り返すたびにi++という処理によって1ずつ増加していきます。これをfor文のブロックから使用すると、0, 1, 2, 3, 4と、繰り返しが進むたびに変化していきます 。つまり、このfor文を展開すると、次のような命令の列で表せます。

リスト: 繰り返しの展開例
int i = 0;
JOptionPane.showMessageDialog(null, i + "回目の表示です");
i++; // i = 1;
JOptionPane.showMessageDialog(null, i + "回目の表示です");
i++; // i = 2;
JOptionPane.showMessageDialog(null, i + "回目の表示です");
i++; // i = 3;
JOptionPane.showMessageDialog(null, i + "回目の表示です");
i++; // i = 4;
JOptionPane.showMessageDialog(null, i + "回目の表示です");
i++; // i = 5;

上記は、iが0から開始して5まで増えていきます。iが5になったらダイアログを表示しないで繰り返しは終了しますので、実際にはiが0, 1, 2, 3, 4のときにダイアログを表示させています。

このように、for文を利用した繰り返しでは、ただ同じ処理を繰り返すのではなく、毎回少しずつ異なる処理を繰り返すこともできます。なお、繰り返しの制御に使う変数を「繰り返し変数」や「ループ変数」などとも呼び、iやjなどの名前 が付けられることがよくあります。

練習: 繰り返し変数

利用するクラスの名前
ShowCount

例のプログラム (ShowCount) を書き換えて「1回目の表示です」「2回目の表示です」…「5回目の表示です」と表示させるプログラムを書きなさい。

配列と繰り返し

配列要素の反復

for文による繰り返しは、配列を処理する際に非常に便利です。プログラムを見てみましょう。

リスト: GreetMembers
package j1.lesson07;

import javax.swing.JOptionPane;

public class GreetMembers {

    public static void main(String[] args) {
        new GreetMembers().start();
    }

    void start() {
        String[] members = getMembers();
        for (int i = 0; i < members.length; i++) {
            JOptionPane.showMessageDialog(null, "こんにちは、" + members[i] + "さん");
        }
    }

    String[] getMembers() {
        String[] members = new String[3];
        members[0] = "Alice";
        members[1] = "Bob";
        members[2] = "Charlie";
        return members;
    }
}

このプログラムでは、配列の各要素をfor文の中で利用しています。また、繰り返し回数をmemebrs.lengthのように配列の長さに合わせているため、配列の長さと同じ回数だけ繰り返しています。

リスト: 繰り返しによる配列要素の利用
String[] members = getMembers();
for (int i = 0; i < members.length; i++) {
    JOptionPane.showMessageDialog(null, "こんにちは、" + members[i] + "さん");
}

getMembersメソッドが返す配列には次のような文字列を記憶させています。このプログラムでは、iを0, 1, 2と順番に増やしながらmembers[0], members[1], members[2]を表示していることになります。

i members[i]
0 Alice
1 Bob
2 Charlie

なお、membersに記憶させた配列の長さは3なので、プログラム中のmembers.lengthも3となります。このため、iが3になったときには処理を行わず、結果としてiが0, 1, 2と配列のインデックスに沿って順番に変化していきます。

このように配列とfor文による繰り返しを組み合わせると、配列の要素を順に利用するようなプログラムを書けるようになります。特殊なことをしない限りは次のように書けますので、パターンとして覚えて しまってもいいかもしれません。

リスト: 配列の要素を順に利用するプログラムのパターン
int[] array = ...
for (int i = 0; i < array.length; i++) {
    ... array[i] ...
}

この配列をfor文で操作するというプログラミング は非常に重要なので、もうひとつ 見てみましょう。先ほどのプログラムのgetMembersメソッドを書き換えて、入力ダイアログから入力する人数や名前を変えられるようにしたプログラムです。

リスト: InputMembers
package j1.lesson07;

import javax.swing.JOptionPane;

public class InputMembers {

    public static void main(String[] args) {
        new InputMembers().start();
    }

    void start() {
        String[] members = getMembers();
        for (int i = 0; i < members.length; i++) {
            JOptionPane.showMessageDialog(null, "こんにちは、" + members[i] + "さん");
        }
    }

    String[] getMembers() {
        String input = JOptionPane.showInputDialog("入力する人数は?");
        int count = Integer.parseInt(input);
        String[] members = new String[count];
        for (int i = 0; i < members.length; i++) {
            String name = JOptionPane.showInputDialog((i + 1) + "人目の名前は?");
            members[i] = name;
        }
        return members;
    }
}

このプログラムでは、配列を生成する際に配列の大きさを入力された値で指定しています。つまり、生成する配列の大きさを入力によって変えられるようになっています。

リスト: 任意の長さの配列を生成する
String input = JOptionPane.showInputDialog("入力する人数は?");
int count = Integer.parseInt(input);
String[] members = new String[count];

そして、配列の各要素に名前を入力する部分では、for文を使ってそれぞれの要素に値を代入 しています。for文の中では毎回ダイアログを表示して、それぞれの名前を入力させています。

リスト: 任意の長さの配列に要素を設定する
for (int i = 0; i < members.length; i++) {
    String name = JOptionPane.showInputDialog((i + 1) + "人目の名前は?");
    members[i] = name;
}

なお、members.lengthは配列を生成した瞬間に決まり、プログラム中のcountと同じ値です。この例のfor文ではmembers.lengthの代わりにcountを指定してもよかった のですが、先ほど紹介したパターンと同じ形式にするためにこのように書きました。

配列要素の集計

繰り返しの最後の例として、配列とfor文を組み合わせた少し高度な例を紹介します。次のプログラムは実行すると何を表示するでしょうか。

リスト: Sum
package j1.lesson07;

import javax.swing.JOptionPane;

public class Sum {

    public static void main(String[] args) {
        new Sum().start();
    }

    void start() {
        int[] values = getValues();
        int total = 0;
        for (int i = 0; i < values.length; i++) {
            total = total + values[i];
        }
        JOptionPane.showMessageDialog(null, total);
    }

    int[] getValues() {
        int[] values = new int[5];
        values[0] = 10;
        values[1] = 20;
        values[2] = 30;
        values[3] = 40;
        values[4] = 50;
        return values;
    }
}

このプログラムで重要な点は、次の個所です。先ほどのfor文の例では配列の要素を順に表示するだけでしたが、今回は配列の要素を集計 して、合計を計算しています。

リスト: 配列要素の集計
int total = 0;
for (int i = 0; i < values.length; i++) {
    total = total + values[i];
}

繰り返しを行う前にtotalの値を0に初期化しています。これは配列に要素が1つもなかった場合の合計を表しています。さらに、変数に値が設定されていないと繰り返し1回目の「total + values[i]」の部分でtotalの値が決まっていないためにエラーになります。

繰り返し変数、配列のそれぞれの要素、totalに記憶させた値などをまとめると、下記の表のようになります。繰り返しを含むプログラムは処理の流れを把握するのが大変なので、慣れないうちは繰り返しごとに変数の状態をリストアップするとわかりやすいと思います。

i values[i] total total + values[i]
0 10 0 10
1 20 10 30
2 30 30 60
3 40 60 100
4 50 100 150
5 150

このように配列の要素を繰り返し処理で集計する、というのは様々な場面で利用します。たとえばオセロのようなボードゲームで盤面上の石を数え上げる 場合などにも、このfor文が利用できます。

練習: 配列要素の集計

作成するクラスの名前
Average

下記のプログラムを元に、入力した実数の一覧に対して平均値を計算して表示するプログラムを書きなさい。

リスト: Average
package j1.lesson07;

import javax.swing.JOptionPane;

public class Average {

    public static void main(String[] args) {
        new Average().start();
    }

    void start() {
        double[] values = getValues();
        
    }

    double[] getValues() {
        String countInput = JOptionPane.showInputDialog("入力する個数は?");
        int count = Integer.parseInt(countInput);
        double[] values = new double[count];
        for (int i = 0; i < values.length; i++) {
            String valueInput = JOptionPane.showInputDialog((i + 1) + "回目の入力");
            double value = Double.parseDouble(valueInput);
            values[i] = value;
        }
        return values;
    }
}

まとめ

すべてのコンピューターの処理は「順次」「分岐」「反復」という構造の組合せで表せる、ということを紹介しましたが、今回は最後の「反復」について説明しました。Javaではfor文を利用して特定の処理を繰り返し行えます。

図: 構造化定理(再掲)

Javaのfor文は次の形式で書きます。ここで宣言した繰り返し変数はfor文のブロック内で利用でき、ブロック内の命令を処理するたびに繰り返し変数が更新されます。また、毎回のブロックを処理する前には繰り返し条件が判定され、条件が成立しない場合には繰り返しが終了します。

for (<変数の宣言>; <繰り返し条件>; <変数の更新>) {
    <命令>
}
図: for文の構造(再掲)

多くの場合、for文は次のような形式で書きます。これは指定した繰り返し回数だけ処理を繰り返します。このとき、変数iに記憶させる値は、順に0, 1, …, (繰り返し回数 – 1)です。

for (int i = 0; i < (繰り返し回数); i++) {
    <命令>
}

この繰り返しを配列と組み合わせると、配列に記憶させた要素を順番に取り出して処理できます。配列の長さにかかわらず同じ書き方ができるので、データの列に対して柔軟に処理を行えます。講義では、配列の中身を合計するプログラムについて紹介しました。

int[] values = getValues();
int total = 0;
for (int i = 0; i < values.length; i++) {
    total = total + values[i];
}

以上が今回のまとめです。「順次」「分岐」「反復」がすべてそろったことで、一通りのプログラムを書けるようになったはずです。