新人エンジニア研修で知っておきたい繰り返し処理の書き方

なぜ、繰り返し処理の理解が重要なのか、その理由。

この記事では、弊社の新人エンジニア研修の参考にJavaを解説します。

前回は条件分岐と判定条件について解説しました。

今回は繰り返し処理について解説します。人間だったら嫌になってしまう繰り返し処理もコンピュータにとっては何でもありません。コンピュータの偉大さを感じる瞬間です。

ここでは、以下の3つの繰り返し処理を学びましょう。

  • while文
  • for文
  • do while文

です。

なお、繰り返し処理のことをループとも言います。

1.while文

英語の【while】には「~の間」という意味がありました。

継続条件を満たしている間は処理を繰り返すというのがwhile文です。

whileの基本構文です。

<構文>

While(継続条件式){
      文;
}

「継続条件式」がtrueなら「文」を処理する。 false ならwhile文を終了する。という意味になります。

Javaで書くと以下Example01のようになります。

package chap05;

public class Example01 {
    public static void main(String[] args) {
        int i = 0;
        while (i < 3) {
            System.out.println(i);
            i++;
        }
    }
}

<実行結果>

0
1
2

ここでは、9行目のインクリメントに着目してください。もしも、このインクリメントがないと、継続条件式がいつまでもtrueのままでループを抜けませんから、無限ループになってしまいます。ぜひ、無限ループも経験しておいてください。そして、無限ループの止め方もIDEごとに異なりますので講師に教わってください。

この繰り返しの文をよく理解するためにはトレース表を埋めてみるのが一番です。途中まで埋まったトレース表の最後の一行を埋めて完成させてください。

 iの値i < 3 の値
6行目0-
1回目の7行目00 < 3 :true
1回目の9行目1-
2回目の7行目11 < 3 :true
2回目の9行目2-
3回目の7行目22 < 3 : true
3回目の9行目3-
4回目の7行目33 < 3 :false
表5.1 While文のトレース

ここでループを抜けています。

なお、前回使ったデバッガを使うと以下のように繰り返しの様子を見ることができます。

新人エンジニア研修

余談ですが、なぜ変数に"i"という文字を使っているかというと、昔のプログラミングスタイルの名残です。興味のある方はググってください。ただ、一時的な使い捨ての変数には短い名前がつけられる傾向があることは、知っておいて損はありません。

2.for文

先のwhile文では、ループに入る前に変数のiを初期化して、さらにループの中でこのiをインクリメントする必要がありました。この構造を見やすく1行で表現できるのがfor文です。英語の前置詞【for】にも~の間という意味がありますが、【for two weeks】のように数値と一緒に使われます。

まずは、下図5.1でイメージを掴みましょう。

新人エンジニア研修向けにwhile文とfor文の比較
図5.1 while文とfor文の比較

基本構文です。

<構文>

for(初期化文; 継続条件式; 更新文){
   文;
}

  • ループに入る前に初期化文を一度だけ行います。
  • 継続条件式がtrueなら文を実行し、falseならループを終了します。
  • 更新文を実行し、2に戻ります。

for文で間違えやすいのは、「継続条件式は繰り返し処理を実行する前に判定されるということです。また、「更新文は繰り返し処理が終わってから実行される」こともポイントです。

下図5.2の順番をしっかり頭に入れましょぅ。

for文の構造
図5.2 for文の構造

 これも以下、Example02で見てみます。

for文の複雑な構文もIDEを使うと簡単に挿入できますので、やり方を講師から聞いてください。

package chap05;

public class Example02 {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            System.out.println(i);
        }
    }
}

<実行結果>

0
1
2

処理内容は先ほどのwhile文と同じですが、シンプルに書けているのがお分かりになると思います。

ループの制御構造が一か所にまとまっているので、繰り返したい処理ブロックが長くなっても可読性が保たれるのがfor文の良いところです。

下表5.2の途中まで埋まったトレースを完成させてください。

 初期化式の値 ii < 3 の値i++の値
1回目の6行目00 < 3 :true-
2回目の6行目
3回目の6行目
4回目の6行目
表5.2 for文のトレース

例題1

次のfor文の繰り返し回数を答えなさい。

for (int i = 0; i < 100; i++) {}
for (int i = 1; i < 100; i++) {}
for (int i = 0; i <= 100; i++) {}
for (int i = 1; i <= 100; i++) {}
for (int i = 0; i < 100; i+=2) {}
for (int i = 100; i > 0; i--) {}

for文とwhile文のどちらを使うか迷ったらまずはfor文を使って下さい。さらに後で学ぶ拡張for文が使える場面では拡張for文を使って下さい。

ここで少し、繰り返し処理というテーマから外れますが新人エンジニア研修で間違えやすい変数の有効範囲(スコープ)ということについて注意喚起です。

例えば、以下Example03のようなプログラムはException 「例外」が出て止まってしまいます。

何が良くないのでしょうか?

package chap05;

public class Example03 {
    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            System.out.println(i);
        }
        System.out.println(i);
    }
}

<実行結果>

(一部省略)
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - Erroneous tree type: <any>

この例外発生の原因は、変数(ローカル変数)の有効範囲は同じブロック内({}波カッコの中)というルールがあるからです。

もしも、変数iをfor文の外でも使いたい場合には、以下Example04のように変数宣言をfor文の外(前)でします。

package chap05;

public class Example04 {
    public static void main(String[] args) {
        int i;
        for (i = 0; i < 6; i++) {
            System.out.println(i);
        }
        System.out.println(i);
    }
}

<実行結果>

0
1
2
3
4
5
6

スコープの問題はこれからも出てきますので、まずは言葉と概念を押さえてください。通常は宣言と{ }の範囲に気を配ればよいのですが、for文の場合は一見、変数宣言が{ }の前でされているように見えるため間違いやすいです。

  • ちなみに、上記の実行結果では最後に6が出力されています。なぜでしょうか?
あなたの答え:

3.ユーザー入力を受け付ける

ここで、繰り返しの話からは少し外れるのですが、ユーザーからの入力ができるようにしてみましょう。

ユーザーからの入力を受け付けるにはjava.util.Scanner標準API)を使います。

try (Scanner sc = new Scanner(System.in)) {

int count = sc.nextInt();

}

※上記サンプルではtry-with-resources構文というものを使用しています。本来Scannerクラスは使用し終わったら閉じる必要があるのですが、この記述のお陰でその手間が省けるのです。また、例外処理の章でお話します。

System.outは標準出力でした。System.inは標準入力です。標準入力とはお使いのパソコンのキーボードのことです。

では、先程の繰り返し処理で繰り返し回数をユーザーが入力するようにするにはどうしたらいいでしょうか?

以下Example05をご覧ください

package chap05;

import java.util.Scanner;

public class Example05 {
    public static void main(String[] args) {
        System.out.println("何回数え上げますか?");

        try (Scanner sc = new Scanner(System.in)) {
            int count = sc.nextInt();

            int i = 0;
            while (i < count) {
                System.out.println(i);
                i++;
            }
        }
    }
}

ここからの解説と問題演習ではこのScannerクラスの機能を使います。

4.ループを中断するbreak文

先ほど、無限ループを説明しました。この無限ループを利用するプログラムがあります。繰り返し回数が分からない場合に while(true) として無限ループにする方法です。「無限ループだといつまでたってもプログラムが終了しないのでは?」と思った人も多いかと思います。ある条件のときだけ無限ループを抜けるようにします。

無限ループとよく組み合わせて使われるのがbreak文です。【break:破る】ということでループを抜けます。前回もswitch文で使いましたね。

具体例で見てみましょう。以下Example06はユーザが入力した数値を足していき、合計値を表示します。ただし、-1が入力されたら終了します。

package chap05;

import java.util.Scanner;

public class Example06 {
    public static void main(String[] args) {

        int n = 0, sum = 0;

        try (Scanner scanner = new Scanner(System.in)) {
            while (true) {
                System.out.println("整数値を入力してください。(終了するには-1を入力)");
                n = scanner.nextInt();
                System.out.println(n + "が入力されました。");
                if (n == -1) {
                    break;
                }
                sum += n;
                System.out.println("これまでの合計" + sum);
            }
        }
    }
}

<実行結果の例>

整数値を入力してください。(終了するには-1を入力)
2
2が入力されました。
これまでの合計:2
整数値を入力してください。(終了するには-1を入力)
4
4が入力されました。
これまでの合計:6
整数値を入力してください。(終了するには-1を入力)
-1


このように、繰り返しの中に(if+break)という形は頻繫に登場しますので覚えてください。

また、while (true)のところをfor(;;)と書いても同じ処理になりますので試してみてください。

上記の例では、(if+break)を使わずにwhile文を使って以下Example07のよう書くことももちろんできます。

しかし、(if+break)を使うと、どういう条件でループを抜けるのかということをより明確に表現できるのです。

package chap05;

import java.util.Scanner;

public class Example07 {
    public static void main(String[] args) {

        int n = 0, sum = 0;

        try (Scanner scanner = new Scanner(System.in)) {
            System.out.println("整数値を入力してください。(終了するには-1を入力)");
            n = scanner.nextInt();

            while (n != -1) {
                sum += n;
                System.out.println(n + "が入力されました。");
                System.out.println("これまでの合計:" + sum);
                System.out.println("整数値を入力してください。(終了するには-1を入力)");
                n = scanner.nextInt();
            }
            System.out.println("終わります");
        }
    }
}

<実行結果の例>

整数値を入力してください。(終了するには-1を入力)
2
2が入力されました。
これまでの合計:2
整数値を入力してください。(終了するには-1を入力)
4
4が入力されました。
これまでの合計:6
整数値を入力してください。(終了するには-1を入力)
-1

ところで、(if+break)で使ったif(n == -1)というのはどういうときにループを終了するかを表現するという意味で終了条件と呼ばれます。一方、Example07のwhile(n != -1)というのは継続条件と呼ばれます。

前回の論理演算子のところで条件の否定について学びました。継続条件の否定は終了条件です。つまり、「age < 20 はお酒が飲めない」を否定すると「age >= 20 はお酒が飲める」になります。このように終了条件と継続条件を相互に変換するには、不等号をひっくり返して=がなければ足し、あれば無くします

つまり、もしも、!(sum > 1000) という条件があったとして、この条件は (sum <= 1000) と等しく、!(sum <= 1000) は (sum > 1000) と等しいという論理になります。

5.ループを1回スキップするcontinue文

continue文はブロック内の残りの文をスキップして次の継続条件判定を直ちに実行する構文です。ただし、continue文を使わなくてもif文で同じことができますからこの新人研修では優先度を下げています。

以下のサンプルコードExample08はユーザが入力した数値を足していき、合計値を表示します。ただし、マイナスの数は合計せずにスキップします。今回はfor文を使ってみましょう。

package chap05;

import java.util.Scanner;

public class Example08 {
	public static void main(String[] args) {

		int n = 0, sum = 0;
		try (Scanner sc = new Scanner(System.in)) {
			for (int i = 0; i < 3; i++) {
				System.out.println("整数値を入力してください。");
				n = sc.nextInt();
				if (n < 0) {
					continue;
				}
				sum += n;
				System.out.println(n + "が入力されました。");
				System.out.println("これまでの合計:" + sum);
			}
			System.out.println("終わります");
		}
	}
}

<実行結果>

整数値を入力してください。
2
2が入力されました。
これまでの合計:2
整数値を入力してください。
-1
整数値を入力してください。
2
2が入力されました。
これまでの合計:4

足し込んでいく処理を書く場所に気をつけてください。continueでスキップされるのは、繰り返しのブロックのうちcontinue文の後ろの部分になります。

6.ループのネスト

ループ処理の中にループ処理を入れることがありますループのネスト、または入れ子処理といいます

nestとは英語で「巣」という意味です。アリの巣のように巣の中に巣があって、その中にも、、、と延々続いている様子をイメージしてください。

例えば、九九を表示することを考えてみます。九九の計算回数はその名の通り9×9で81回ですね。1の段は、掛けられる数(1)を固定してその数に1~9までの数を繰り返し掛けます。2の段は、掛けられる数を1増やして(2になる)その数に1~9までの数を繰り返し掛けます。以降同様にかけられる数は9まで繰り返し増やして処理を継続します。つまり、繰り返しの中に繰り返しがあります。

Javaのコードでは例えば、以下Example09のようになります。

package chap05;

public class Example09 {
    public static void main(String[] args) {
        for (int i = 1; i <= 9; i++) {
            for (int j = 1; j <= 9; j++) {
                System.out.print(i * j + "\t"); //最後のタブは見やすさのため
            }
            System.out.println(""); //改行のため
        }
    }
}

<実行結果>

1	2	3	4	5	6	7	8	9	
2	4	6	8	10	12	14	16	18	
3	6	9	12	15	18	21	24	27	
4	8	12	16	20	24	28	32	36	
5	10	15	20	25	30	35	40	45	
6	12	18	24	30	36	42	48	54	
7	14	21	28	35	42	49	56	63	
8	16	24	32	40	48	56	64	72	
9	18	27	36	45	54	63	72	81

下図5.3はループのネストのイメージです。

新人エンジニア研修でループのネストのイメージを説明
図5.3 ループのネストのイメージ

青色の外側のループが9回まわりオレンジの内側のループが9回まわって合計81回の掛け算が行われたことが分かればループのネストはOKです。

例題2

次の繰り返し構文の内側の繰り返し回数を答えなさい。

for (int i = 0; i < 10; i++) {
  for (int j = 0; j < 10; j++) {
  }
}
for (int i = 10; i > 0; i--) {
  int j = i - 1;
  while (j >= 0) {
   j--;
  }
}

7.do while文

最後にwhile文やfor文ほど使用頻度は高くありませんが、もう一つのループ処理の記述方法であるdo while文を簡単に見ておきましょう。

ただし、この新人エンジニア研修ではほとんど出番はありません。

do while文の構文

<構文>

do {
    文;
} while(継続条件式);

  • まず無条件に1回、文を実行します。
  • 継続条件式がtrueなら1に戻り、falseならループを抜けます。

具体例で見てみましょう。以下Example10を御覧ください。

package chap05;

public class Example10 {
	public static void main(String[] args) {

		int count = 0;
		do {
			count++;
		} while (count < 0);
		System.out.println(count);
	}
}

<実行結果>

1

このように、無条件に1回、文を実行するのがdo while文です。

do while文の条件式の後には、文末ですのでセミコロンが必要です。しかし、通常のwhile文の()の後にセミコロンをつけてしまうと空文だと解釈されて、何も処理されなくなってしまいます。初学者が犯しやすいミスなので、気をつけてください。もっともIDEを使っていればミスを指摘してくれはしますが。

まとめ:隣の人に正しく説明できたらチェックを付けましょう

□ 継続条件を満たしている間は処理を繰り返すというのがwhile文やfor文である

□ for文で間違えやすいのは、「継続条件式は繰り返し処理を実行する前に判定される」、「更新文は繰り返し処理が終わってから実行される」ということである

□ break文はループを中断し、continue文はループを1回スキップする

□ 終了条件と継続条件を相互に変換するには、不等号をひっくり返して=がなければ足し、あれば無くす

□ ループ処理の中にループ処理を入れることをループのネスト、または入れ子処理という

まとめができたら、アウトプットとして演習問題にチャレンジしましょう。

以上、今回は「繰り返しで単純作業をコンピュータに任せる」方法について見てきました。

次回は、「配列を使い大量データを便利に扱う」方法を学びます。

繰り返しで単純作業をコンピュータに任せる 最後までお読みいただきありがとうございます。