Javaプログラミング基礎 講義資料

for 文を用いた繰り返しプログラム

プログラムには、文を何回も繰り返す機能があります。 これまで、似たような処理を何回も書かなければならない例題や、 演習問題がありましたが、 繰り返しの機能を使うと簡潔に書くことができます。

繰り返しを用いたプログラムの書き方として、 今回は for 文を取り上げます。

for 文

教科書 pp.125-129 も同時に参照してください。

for文の構造

for 文は繰り返しの前に、式1 を一回だけ実行します。 これは、繰り返しの前の準備作業と言えます。 通常、ここには変数への初期値の代入など、初期化処理を書きます。 (教科書では「初期化」)

式2 は、繰り返しを続けるかどうかの判断を表す式を書きます。 式2 の条件が満たされている間繰り返しを行います。 (教科書では「条件式」)

式3 は、繰り返しを1回実行したあとに次の回に進めるための操作を書きます。 通常は、繰り返しの次の回に移る際に変数の値を変化させる操作等を行います。 (教科書では「次の一歩」)

また、式1 〜式3 は、いずれも省略することができます。 ただし、セミコロン (;) は省略できません。 式1 や式3 を省略した場合単に何もしないことになるのですが、 式2 を省略すると無限に繰り返しを行うことになります。 この場合は別の方法で、繰り返しを止めるようにしなければなりません。

for文の典型的な使い方は以下のようなプログラムです。 (教科書 p.126 List6-2 とほぼ同じ内容)

public class ZeroOneTwo {
    public static void main(String[] args) {
        int i;
        for (i = 0; i < 3; i++)
	    System.out.println("iの値は " + i + "です");
    }
}

繰り返しを用いたプログラムでは、 文法的な知識として覚えることはそれほど多くありません。 それよりも、プログラムの書き方についての感覚をつかむことの方が重要です。 また、変数をどう使えば良いかも要点になります。 ここでは、プログラムの例を読むことによって、 繰り返しを使ったプログラミングの感覚をつかみましょう。

例題: 1から3までの合計を求める

繰り返しのあるプログラムはプログラムの同じ場所を 何回も実行することになります。 同じ場所を通るたびに変数の値が少しずつ変わり、 最終的に結果が求まります。 ここで、各変数の値はある規則にしたがって変化していくことを きちんと理解する必要があります。

次の例題を用いて、プログラムの理解の仕方を説明しましょう。 プログラムの名前は SumOneToThree としています。

public class SumOneToThree {
    public static void main (String[] args) {
        int sum = 0;

        int i;
        for (i = 1; i <= 3; i++)
            sum = sum + i;

        System.out.println("1 から 3 までの和は " + sum);
    }
}

このプログラムは 1 から 3 までの和を求めて表示するものです。 結果は 6 となります。

まず、下の下線の部分に注目してみましょう。

for (i = 1; i <= 3; i++)
    sum = sum + i;

この繰り返しのための準備を行う式は i = 1 です。 繰り返しは、「変数 i1」から始まることになります。

次の i <= 3 は、 この for 文の繰り返しを続けるかどうかの条件です。 i <= 3 を満たしている間、すなわち「i が 3 以下のとき」 繰り返しを続けることになります。

さらに次の、 i++ は、繰り返しの中身を 1 回実行したあとに 次の回に進めるための式です。 この場合、「繰り返し 1 回ごとに i を 1 増やす、」 ということを行います。

以上から、この for 文は 「i が 1 から 3 まで 1 ずつ増えながら 3 回の繰り返しを行う。」 と解釈することができます。

次に、for 文の中身に注目します。

for (i = 1; i <= 3; i++)
    sum = sum + i;

for の中身 sum = sum + i は、 繰り返しの毎回で行う計算の内容です。

繰り返しの中身の動きを追ってみましょう。 繰り返しは i が 1,2,3 と変化しながら 3 回行われるわけですから、 sum の式によって変数の値は次のように変化します。

初期値:  sum = 0 , i = 1

1回目:   sum = sum ←(0) + i ←(1)      → iは1 sumは1
         i++
2回目:   sum = sum ←(1) + i ←(2)      → iは2 sumは3
         i++
3回目:   sum = sum ←(3) + i ←(3)      → iは3 sumは6
         i++

ここで i <=3 を満たさなくなるので繰り返しは終了

1から3までの合計が計算できた

このようなトレースは最も単純で明快な方法と言えます。 例を使ってプログラムを理解する方法は、初心者には一番分かりやすいでしょう。 しかし、プログラムが複雑になり多数の変数や条件が絡み合ってくると、 この方法での理解は限界があります。

変数の意味を言葉で述べる

プログラムの理解の鍵は、変数の意味をしっかり把握することです。 「プログラムのこの地点では、この変数にこういう値が入っている。」 と言えるようにするのです。たとえば、x, y, z の最大値を求めるプログラムも、 この原則を適用すると理解の助けになるはずです。

例題のプログラムでは、繰り返しの中に 2 つの変数 isum が現れます。 変数 i は 1 から 3 まで 1 ずつ増えていきます。 これはすぐに分かります。 1 から 3 までの和を求めるのですから、 1 から 3 までの値を 1 回ずつ登場させるのは当然です。

変数 sum の方はどうでしょう。 「sum の値は 1 から i までの和が入っている。」 と説明できます。

以上から、この繰り返しでは、 「i を 1 ずつ増やしながら途中までの和を求め、 その値を sum に代入している。」と説明することができます。

厳密には、sum = sum + i が実行された直後の sum は 1 から i までの和で、 繰り返しの中身が終わった段階 (i <= 3 の判定が行われる段階) では、 sum は 1 から i-1 までの和です。 また、少し考えると for 文から抜けたときの i の値は、 4 であったことがわかります。 ここでも sum が 1 から i-1 の和という意味が成立しています。 i を 4 で置き換えると、 最終的には sum は 1 から 3 までの和ということになり、 所期の目的が達成できていることが分かります。

このように、変数に入っている値の意味を言葉で言えるようにすることが重要です。 プログラムを書くときは、この考え方を逆に利用します。 最初に、プログラムのある地点で、 変数にこんな意味の値が入っているようにしようと決め、 その値を変数に入れるにはどんな計算をすれば良いかを考えるのです。

さまざまな for 文

前の例題は変数 i が 1 から 1 刻みに増える for 文でした。 次のような for 文も考えられます。

0 から 9 まで 1 ずつ増やしながら 10 回繰り返す

for (i = 0; i < 10; i++)

0 から 8 まで 2 ずつ増やしながら 5 回繰り返す

for (i = 0; i < 10; i = i + 2)

10 から 1 まで 1 ずつ減らしながら 10 回繰り返す

for (i = 10; i > 0; i--)

0 から n-1 まで 1 ずつ増やしながら n 回繰り返す

for (i = 0; i < n; i++)

変数宣言

これまで示したプログラムでは、冒頭で変数宣言を行っていました。 実は、 Java ではプログラムの任意の位置で変数宣言を行うことができます。 例えば、次のような for 文を書くこともできます。

for (int i = 0; i < 10; i++)
    ....

ただし、プログラムのどこからでも変数が使えるわけではありません。 変数は、宣言したもっとも近い中カッコの範囲内でのみ有効です。 中カッコの範囲内のことをブロックと呼び、 変数の有効範囲のことをスコープと呼びます。

例えば for 文の中カッコの内側で宣言した変数は、 繰り返しの外では使うことができません。 次は間違いの例です。

for (int i = 0; i < 5; i++) {
    int sum = sum + i;
    System.out.println(i + " " + sum);
}

System.out.println("合計は " + sum);

変数 sum は for 文の中カッコ内で宣言されているため、 その外側で使うことはできません。正しくは sum を for 文の外側で宣言し、 次のように書きます。

int sum = 0;
for (int i = 0; i < 5; i++)
    sum = sum + i;

System.out.println("合計は " + sum);

(参考: 教科書 pp.138-139)

変数宣言を行う際には、どの変数がどういう意味で使われているか 把握しやすい位置で宣言すると良いでしょう。 例えば、その変数を使う直前で宣言するようにしたり、 あるまとまった処理についての変数であれば、 その処理の冒頭に変数宣言をまとめておく等の方針が良いでしょう。

文の組み合わせ

for 文も if 文も「文」の一種です。 また、繰り返しの中身や条件判定の中身に書くことができるのも「文」です。 したがって、これまで学んだ、for 文、 if 文、式文、変数宣言式文などは すべて自由に組み合わせることができます。

0からnまでの偶数の合計を求める

public class SumOfEvenNumbers2 {
    public static void main(String[] args) {
        int n = 10;
        int sum = 0;

        for (int i = 0; i <= n; i++) {
            if (i % 2 == 0) 
                sum = sum + i;
            System.out.println("0 から " + i + " までの偶数の合計は " + sum);
        }
    }
}

「式」についてのおさらい

Java では四則演算のような計算以外にも、 多くのものを「式」として扱います。 式は数値や文字や、条件が成り立つ/成り立たないなどの 何らかの値を持っています。

例えば、次のようなものはすべて式です。

1
a + b
"Hello world."
"max = " + a
a > 5 && a == b
a = 1

実は if 文や for 文に書く条件や代入も式の一種なのです。 for 文のカッコの中には 3 つ式を書くわけですが、 代入をその一つとして書けるのは、 Java が代入を式として扱っているからです。

Java における式の演算子にどのような種類があるのかは、 教科書 pp.334-335 にまとめてあります。 ここで、優先順位が高い演算子とは、 他の演算子より先に計算が行われるという意味です。

条件が成り立つ/成り立たないことを表現する boolean 型

教科書 pp.85-87 にも少しだけ記述があります。

if 文や for 文で使われる条件式は、 ある条件が成り立つか成り立たないかを判定する式でした。 この「成り立つか成り立たないか」という情報そのものも 変数に入れておくことができます。

例えば、次の式は x が正であるという条件を満たしているか、 満たしていないか、言わば Yes か No かという値を condition に代入しています。このような値を真偽値 (truth value) と言います。

condition = x > 0;

Javaでは 真偽値は true (真、すなわち条件が成立している) または false (偽、すなわち条件が成立していない) のどちらかの値をとります。 また、真偽値を boolean 型の変数に入れておくことができます。

boolean condition1;
boolean condition2;

condition1 = x == y;
condition2 = y == z;

if (condition1 && condition2) {
    // x と y が等しい、かつ y と z が等しい
}

boolean 型の変数は、 場合分けのための目印のような役割をさせることもできます。 以下は、1898年から1903年までにうるう年が 1つでもあったかどうかを判定するプログラムです。

public nclass LeapYearSearcher {
    public static void main(String[] args) {

        boolean isLeapYear = false;    // うるう年があったかどうかを示す目印

        for(int i = 1898; i <= 1903; i++) 
            if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0)
                isLeapYear = true;     // 繰り返し中にうるう年が1つでもあったら true にする

        if (isLeapYear) 
            System.out.println("うるう年がありました");
        else 
            System.out.println("うるう年がありませんでした");
    }
}

式に関するあれこれ

代入のイコールも演算子の一つなので、 式の中の要素として書くことができます。 例えば、

w = x + (y = z + 1);

などという式を書くことができます。 この式の意味は、 yz + 1 を代入して、 その値に x を加えたものを w に代入する、という意味です。 代入の式 (y = z + 1) 自身も値を持っており、 それは z + 1 の値になります。

このような書き方をすると、複雑な式を簡潔に書けることがあります。 例えば、以下のようなプログラム片、

if ((x = y - 1) == 0 )
    ....

は、y - 1 の値を x に代入し、 その値が 0 であれば if 文の中身を実行する、ということを示しています。

ただし、初心者にはこの書き方はあまり勧められません。 プログラムが分かりにくくなり、間違いを犯す可能性が増えるからです。 最初は、なるべく単純な書き方でプログラムを書くようにする方が良いでしょう。