繰り返しを用いたプログラムの書き方として、 今回は for 文を取り上げます。 if 文、while 文、 for 文をマスターできれば、 さまざまな処理を記述することができ、プログラミングの世界が広がります。
前回の while 文の使い方を思い出してください。 for 文の動作は次の while 文と同等に考えることができます。
式1; while ( 式2 ) { 文 式3; }
for 文の書式は以下のようなものです。
for ( 式1 ; 式2 ; 式3 ) 文
for 文は繰り返しの前に、式1 を一回だけ実行します。 これは、繰り返しの前の準備作業と言えます。 通常、ここには変数への初期値の代入など、初期化処理を書きます。
式2 は、while 文での制御式 (条件式) と同じものです。 繰り返しを続けるかどうかの判断を表す式を書きます。
式3 は、繰り返しを1回実行したあとに次の回に進めるための操作を書きます。 通常は、繰り返しの次の回に移る際に変数の値を変化させる操作等を行います。
また、式1 〜式3 は、いずれも省略することができます。 ただし、セミコロン (;) は省略できません。 式1 や式3 を省略した場合単に何もしないことになるのですが、 式2 を省略すると常に true (真) の意味になり、 無限に繰り返しを行うことになります。この場合は繰り返しの途中に break; を書き、 繰り返しを止めるようにしなければなりません。
例えば、次のプログラム片を見てみます。
s = 0; for (i = 1; i <= n; i++) s = s + i;
このプログラムの働きは 1 から n までの和を求めることです。
まず i に 1 を代入して、i <= n を調べます。 i が n 以下であれば、繰り返し文本体である s = s + i; を実行します。 次に i に 1 を加え、また i <= n を調べてから繰り返しを行います。
結局、この for 文では、 i に 1, 2, 3, ..., n と順次値を代入しながら 繰り返しの本体を実行することになります。
while 文を用いた同じ働きのプログラムは前回の例題に載せてありましたね。
s = 0; i = 1; while (i <= n) { s = s + i; i++; }
2つのプログラムを見比べてみると、 「i を 1 から n まで 1 ずつ増やしながら繰り返す」 という論理を分かりやすく表しているのは for 文の方です。
while 文の方は、i++; が離れた位置にある (この場合はわずか 2 行ですが、 一般にはもっと離れることが多いのです) ので、 繰り返しに関係があるかどうか見分けることが難しいのです。 また、s = 0; や s = s + iが繰り返しの中身の仕事であり、 一方、i = 1; や i++; が繰り返しの制御に関係している、 ということを一目で見抜くことは難しいと言えます。
前回は二重の while 文を用いて x と y の値を変化させながら繰り返すプログラムを書きました。 今回は for 文で書くことを考えてみます。 ファイル名は GraphWithFor.java とします。
x についての for 文は x を -8 から 8 まで 1 ずつ 変化させ繰り返すというものです。 y についての繰り返しも同様です。 変数 i を用意して、 この変数の値を 1 から y まで増やしていきます。
for 文が有利なケースとは、 あらかじめ繰り返す回数が決まっているような場合であると言えるでしょう。 例えば、 1, 2, 3, ..., 100 の順に値を代入しながら繰り返すといった場合には 適していると言えます。
一方、繰り返しの中で行う処理の結果によって繰り返すか、 打ち切るかを決めるようなケースでは、 while 文の方が適当だと言えます。 例えば、前回取り上げた素数かどうか判定するプログラムでの繰り返しは、 「割り切れなければ繰り返し、割り切れたら素数ではないと判断して打ち切る」 というものでした。 このような場合は while 文の方がうまくプログラムを書けるでしょう。
さまざまなプログラムの読み書きを経験し、 これらの使い分けのテクニックを身につけるのです。
これまで示したプログラムでは、冒頭で変数宣言を行っていました。 実は、 Java ではプログラムの任意の位置で変数宣言を行うことができます。 例えば、次のような for 文を書くこともできます。
for (int i = 0; i < 10; i++) ....
変数宣言を行う際には、 どの変数がどういう意味で使われているか 把握しやすい位置で宣言すると良いでしょう。 例えば、その変数を使う直前で宣言するようにしたり、 あるまとまった処理についての変数であれば、 その処理の冒頭に変数宣言をまとめておく等の方針が良いでしょう。
Java では四則演算のような計算以外にも、 多くのものを「式」として扱います。 式は数値や文字や true/false など 何らかの値を持っています。
例えば、次のようなものはすべて式です。
1 a + b "Hello world." "max = " + a a > 5 && a == b a = 1
実は if 文や while 文の条件に書く比較 や代入も式の一種なのです。 for 文のカッコの中には 3 つ式を書くわけですが、 代入をその一つとして書けるのは、 Java が代入を式として扱っているからです。
Java における式の演算子にどのような種類があるのかは、 「絵本」p.36 ページにまとめてあります。 ここで、優先順位が高い演算子とは、 他の演算子より先に計算が行われるという意味です。
代入のイコールも演算子の一つなので、 式の中の要素として書くことができます。 例えば、
w = x + (y = z + 1);
などという式を書くことができます。 この式の意味は、 y に z + 1 を代入して、 その値に x を加えたものを w に代入する、という意味です。 代入の式 (y = z + 1) 自身も値を持っており、 それは z + 1 の値になります。
このような書き方をすると、複雑な式を簡潔に書けることがあります。 例えば、以下のようなプログラム片、
if ((x = y - 1) == 0 ) ....
は、y - 1 の値を x に代入し、 その値が 0 であれば if 文の中身を実行する、ということを示しています。
ただし、初心者にはこの書き方はあまり勧められません。 プログラムが分かりにくくなり、間違いを犯す可能性が増えるからです。 最初は、なるべく単純な書き方でプログラムを書くようにする方が良いでしょう。
条件を調べて場合分けを行う方法として if 文を学びました。 基本的にはどのような場合分けも if 文を用いれば書くことができます。 しかしながら、 if 文で書くと、 不必要に煩雑になって間違いをしやすくなる場合があります。
次のプログラムは与えられた月に対して、 その月が何日あるかを表示するプログラムです。
class DayOfMonth1 { public static void main (String[] args) { int n = 3; if (n == 1) System.out.println(31); else if (n == 2) System.out.println(28); else if (n == 3) System.out.println(31); else if (n == 4) System.out.println(30); else if (n == 5) System.out.println(31); else if (n == 6) System.out.println(30); else if (n == 7) System.out.println(31); else if (n == 8) System.out.println(31); else if (n == 9) System.out.println(30); else if (n == 10) System.out.println(31); else if (n == 11) System.out.println(30); else System.out.println(31); } }
この書き方は、理解のしやすさや間違いの可能性を考えると、 あまり良い書き方とは言えません。 例えば、 if 文の一か所を if (m == 3)等と書いてしまったとします。 本来 n と書くべきところを m と書いてしまったというわけです。 人間は変数 nによる場合分けだと思い込むと、 一つだけ間違っていても見過ごしやすく、やっかいな問題になることがあります。
このような場合分けを、分かりやすく書くことができるのが switch 文です。 上のプログラムを switch 文で書くと次のようになります。
class DayOfMonth2 { public static void main (String[] args) { int n = 3; switch (n) { case 1: System.out.println(31); break; case 2: System.out.println(28); break; case 3: System.out.println(31); break; case 4: System.out.println(30); break; case 5: System.out.println(31); break; case 6: System.out.println(30); break; case 7: System.out.println(31); break; case 8: System.out.println(31); break; case 9: System.out.println(30); break; case 10: System.out.println(31); break; case 11: System.out.println(30); break; default: System.out.println(31); } } }
switch 文の中には「 case 定数式: 」という形式が並んでいます。 switch の直後のカッコ内の式がその定数式に等しければ、 その case 文の直後の文から実行します。 case の定数式には、 == や <= などを含む比較式を書くことはできません。
上の例では、例えば n の値が 2 であれば、 case 2:の直後に飛んで、 System.out.println(28); を行います。
また、どの case にも当てはまらない場合は default: 以下を実行します。
case 文に対する実際の処理を書く部分 (例では System.out.println(....); と書いた部分) には、 複数の文を並べて書くことができます。 これは、今まで習った if 文, while文, for 文のように中身に一つの文しか書けず、 複数の文を書くには中カッコ (「{」,「}」) で囲まなければならない、 という原則とは異なっています。
それぞれの case: の処理の後には break 文を書くことが必須です。 この break 文は switch 文からの脱出を意味するものです。 この break 文がないと、ある case 文以下の処理を実行した後、 さらに次の case 文以下の処理まで実行してしまうことになります。 多くの場合これは間違いだと言えます。 case 文以下の処理の最後には break 文を書く、 ということを覚えてしまうのが良いでしょう。
実は、一つの場所に case 定数式: を二つ以上並べて書くこともできます。
class DayOfMonth3 { public static void main (String[] args) { int n = 3; switch (n) { case 2: System.out.println(28); break; case 4: case 6: case 9: case 11: System.out.println(30); break; default: System.out.println(31); } } }
これは、ひと月の日数が 28 日の 2 月と、 30 日の 4, 6, 9, 11 月と、 それ以外の 31 日ある月で場合分けした例です。
switch 文は、実際のプログラムでは登場する機会がそれほど多くはないのですが、 このような多数の場合分けを行う場合に威力を発揮します。