コンピュータが得意なことの1つに、指示された事を忠実に何度も繰返して実行することがあります。今回は繰返し実行についてみていきます。JavaScriptでは、幾つかの繰返しのための制御文が用意されています。順にみていきましょう。
まず始めにwhile文を紹介します。while文はある条件が成り立っている間、処理を繰り返すと言うものです。書式は次のようになります。
while ( 継続条件 ) { /* * 継続条件が成立する間、実行を繰り返す処理ブロック */ }
書式は単純なif文に良く似ていると思います。実際、「継続条件」の所に書く式はif文で使った条件式と同じものです。while文では、まず継続条件を評価し、その継続条件が成立したら中括弧でくくられたブロックを実行します。ここまでは動作もif文と同じです。その後、中括弧でくくられたブロックを実行し終わると、綴じ中括弧の後ろを実行するのではなく、一旦、継続条件の評価に戻ります。ここがif文と異なる所です。フローチャートで描くと次のようになります。
では、実際に簡単な例を使って動作を確認してみましょう。下記は、1から始めて順に値を2倍にして表示していくプログラムです。値が1000未満である限り繰返します。
<script type="text/javascript"> var n = 1; while (n < 1000) { document.write(n + "<br />"); n = n * 2; } </script>
このプログラムでは、始めにnを1にした後にwhile文に入ります。while文の中ではnが1000より小さい場合には、まずその数字を表示し、その後nを2倍して次の回に備えています。while文を1回目に実行する時はnは1ですが、をのwhile文が終わる時には「n = n * 2」によってnが2倍され2になります。nが2でも1000よりは小さいので、もう一度while文の中括弧でくくられたブロックが実行されます。ここでは2と表示をした後に、「n = n * 2」が実行されnが4になります。その後、nが1024になるまで同じ部分が繰り返されます。nが1024に成った時点で4行目のwhile文の条件が成立しなくなりますので、while文の後のブロックを抜け、プログラムは終了します。
繰返し条件を間違えると延々と同じことが繰り返され、プログラムが終了しない状態になることがあります。このような状態のことを「無限ループ」と表現したりします。無限ループ状態になると、ブラウザが応答しなくなるかもしれません。この場合は、一旦ブラウザを終了してください。
繰返し文の中でループ変数は自己参照代入文を用いて、値を変えていきます。そして、最終的には、 while文の丸括弧の中に書かれた条件(これを「継続条件」と呼びます)を満たさなくなると、 while文は終了して、次の処理を行なうようになります。 この条件繰返し文を用いて、n回繰返しができる記述の仕方を考えてみましょう。 次のスクリプトは、7回繰り返します。
var n = 1; while ( n <= 7 ) { document.write( "いちめんのなのはな <br>" ); n = n + 1; } document.write( "かすかなるむぎぶえ <br>" ); document.write( "いちめんのなのはな <br><br>" ); document.write( " 山村暮鳥<br>" );
さて、このプログラムの例でもありますように、n回繰返しさせるためには、条件繰返し文に対して 繰返しをさせるための整数の変数を用意しなければなりません。 このような変数を「ループ変数(Loop Variable)」と呼びます。 ループ変数を使って、指折り数えながら何回か繰返しを勘定させます。
一般にループ変数を用意してn回繰返しをさせるには、次のように記述します。 下記のnの部分は、必要に応じて、10や20などの正の整数で置き換えてください。
var ループ変数 = 1;
while ( ループ変数 <= n ) {
繰り返したいことをここに記述する
ループ変数 = ループ変数 + 1;
}
ループ変数の名前としては、a, b, c, d, e, f, g, h, i, j, k, m, nあるいはp, q, r, s, t, u, v, w, x, y, zなどの 1文字の変数名(oとlは、数字と間違えやすいので避けるべきです)にするか、 count, counterなどの適当な名前を選んでください。
繰返しのブロックの中では、ループ変数の値を利用して、様々な制御を行なうことができます。 たとえば、次のスクリプトは、19回繰返しを行ないます。 イコールがついていないので、20回でないことに注意してください。 変数名のnがdocument.writeの中に現れていますので、繰返しの中で移り変わってゆく ループ変数の値を確かめることができます。
var n = 1; while ( n < 20 ) { document.write( n + " "); n = n + 1; }
上記の繰返しの記述を実行させれば、HTMLとして次のような表示を得ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
次の例では、ループ変数をiとして、その2倍と7倍を表示させています。
var i=1; while ( i <=12 ) { // 12回繰り返す document.write( i + "の2倍は" + i * 2 + "、7倍は" + i * 7 + "<br>"); i = i + 1; }
次の例でも、ループ変数をiとしていますが、今度はその2乗の値を表示させます。
var i=1; while ( i <=20 ) { // 20回繰り返す document.write( i + "の2乗は" + ( i * i ) + "です。<br>" ); i = i + 1; }
if文と同様に、while文の条件式の指定の丸括弧の直後にセミコロンを書いてはいけません。 コンパイラは、この記述の場合は空文があり、制御文は、そこで強制的に終わりになって、 次に別の新たなブロックが続いているとして解釈して、エラーを出してきません。
var m = 1; while ( m < 30 ) ; { // while文がセミコロンの前で終わるので永遠の繰返しになってしまいます。 // このブロックは前のwhile文とは関係ないただのブロックです m = m + 1; }
while文の条件式(論理式)に書かなければいけないのは「継続条件」です。つまり、その「条件が満足されている間」繰返しが 行なわれる訳です。この継続条件は、繰返しのたびにチェックされます。 間違いやすいのは、while文の条件式の中に、「逆にこのような状態になったら終了するという条件」を記述してしまうことです。 そのような条件を「終了条件」と呼びます。以下の例で書かれているのは「終了条件」です。 「終了条件」のちょうど反対が「継続条件」になっていることに注意し下さい。
var m = 1; while ( m > 30 ) { // 30を超えたら終了したいつもり // このブロックは1回も実行されません m = m + 1; }
簡単な回数繰返し文では、ブロックの中でループ変数で+1していただけなので、何回繰り返すかは、一目瞭然でした。 たとえば、次のプログラムの断片は、ループ変数の値を0から始めていますが、20回繰り返されることが見てわかると 思います。
var m = 0; while ( m < 20 ) { // 20回繰り返されます document.write( m +"<br>"); m = m + 1; }
しかし、一般的な回数繰返し文については、何回ぐらい繰り返すのかをプログラマが想定しておく必要があります。 その想定が間違っていたり、予期していない状況下で繰返しに突入すると、1回も繰り返しされなかったり、 永遠に繰り返し続けられてしまうことになります。 ここでは、一番簡単に、指折り数えていく繰返しの場合、何回繰り返しされるのかについて、予測を立ててみましょう。 変数を1つずつ変化させていく場合は、すぐに回数はわかります。 しかし、差分が1ではない場合(つまり+1していかない場合)は少しやっかいです。 先ほどのプログラムについて、初期値をinitial、最終値をlimit、ループ変数に足し込まれていく数(つまり差分)を diffと変数で置き換えてみました。これがループ変数に足し込んでいく形での一般的な条件繰返しの形になります。
var n = 初期値; ループ変数に初期値を代入
while ( n < 最終値 ) { 最終値未満の間、繰り返す
繰り返されること
n = n + 差分; ループ変数に差分だけ足し込んでいる
}
この場合に、繰返しは何回おこなわれるのでしょうか。 たとえば、初期値が7、最終値が24、差分が4とすると、ループ変数nは次のように推移します。
7→11→15→19→23繰返し終了時のnの値は27で、継続条件を満足しませんので、計5回の繰返しが行なわれることになります。 一般的には、このような繰返しの場合は次のような回数になります。
⎡ (最終値 − 初期値)/差分 ⎤
この⎡と⎤は、数学上ではCeilingと呼ばれている関数です。 実数のところで、コラムの中で取り上げますが、 「整数除算で割り切れる場合はその結果の整数、割り切れない場合はそれよりも1つ大きな整数になる」 という感じで考えてください。 また、上記の記述で/は整数除算です。たとえば先ほどの場合は、⎡ (24 - 7 ) / 4 ⎤ですから、 17 / 4 = 4.25で整数としては割り切れませんから、1つ大きな数になって、5というのがでてきます。 しかし、これはあくまでもwhile文の条件の不等号が<の場合です。 <=の場合はまた違った計算式が必要となります。
先ほどの場合の繰返しを実際にプログラムの断片として記述してみましょう。
var m = 7; while ( m < 24 ) { document.write( "value of m is " + m + "<br>" ); m = m + 4; }
上記のプログラムでも行なっているように、 ループ変数を「document.writeでHTMLに表示させる」ことが繰返しをうまく処理させるのに大きなヒントとなります。 このように、ループ変数の値を追いかけることを「変数をトレースする」と呼びます。 繰返しを作る場合、うまく動いていない(コンパイラは通るのに実行結果がなにか違う)と思ったら、 トレースをすることを忘れないでください。 このようにループ変数や、一般の変数の値を、実行途中にターミナルに表示させることを「デバッグ・プリント」と 呼ぶことがあります。これだけで、熟練プログラマは論理的な間違いを見つけます。 あるいは、先ほどのようにデバッガを使って繰返しの途中で止める機能を用います。
条件繰返し文では、条件が満足されている間は繰返される訳ですから、さまざまな形での繰返しが可能です。 回数繰返し文の例では、ループ変数は足し算で値を足し込まれていました。 しかし、別にループ変数を変化させるのは、 足し算でも引き算でも、掛け算でも割り算でも、剰余算でも、それらをいくつかあるいは全部組み合わせても構わないわけです。 また条件も単なる不等号を使っていましたが、等号でも構いませんし、論理式を使っても構いません。 以下にいくつかのいろいろな形の自己参照代入分を用いた、条件繰返し文の例を記述してみます。 それぞれ、startメソッドの中だけを記述しています。
//ループ変数を引いていく例 var n = 50; while (n > -50 ) { document.write( n+"<br>" ); n = n - 10; }
//ループ変数を掛けていく例(2乗していく) var m = 1; while ( m < 100000000 ) { document.write( m+"<br>" ); m = m * m; }
//ループ変数を割った繰返しの例 var angle = 120; while ( angle > 0 ) { document.write( angle + "<br>" ); angle = (angle - angle % 2) / 2; // 整数除算 }
angleの値が、繰返しのたびに、120→60→30→15→7→3→1→0と変化しているのがわかります。
更に有用な例を見ていきます。
//等号(あっていない)で繰返し条件を繰り返す例(割り切れる数「約数」を見つける) var m = prompt( "2以上の整数を入力してください", 8 ); var n = 2; //どの数でも1では割り切れるため while (m % n != 0 ) { n = n + 1; } document.write( m + "は、" + n + "で割り切れました<br>" );
//それぞれの桁を表示していく var n = prompt( "正の整数を入力してください", 6789 ); var i = 1; while ( n > 0 ) { document.write( i + "桁目は、" + ( n % 10 ) + "です。<br>" ); n = (n - n % 10 )/ 10; // 整数除算 i = i + 1; }
while文を永遠に繰り返さない、一定の秩序ある状態で動かすためには、次のようなことを考える必要があります。
・ループ変数を使って、最初は繰返しの条件を満足し、繰返しに突入するような初期状態にしておく。
・ループ変数を繰返しの中でのブロック内で自己参照しながら変化させるようにする。
・繰返しを継続するための条件は、最終的に満たされないように考える。
このようなことを考えないと、条件繰返し文を使った繰返し、 つまり一般的なプログラミング言語における繰返しは、 まともに機能しないことになります。 たとえば、19回繰り返す例についても、ループ変数nの初期値は1ですから、 「n < 20」という条件を満足し、繰返しブロックに突入することができます。 次に、繰り返されるブロックの中では、 ループ変数の値を自己参照によって変化させています(つまり+1していること)ので、 ループ変数の値が変化していくことになります。 最終的に変数nの値は、増え続け、20以上になりますので、 「n < 20」という条件を満足しなくなり、繰返しは終わり、 その次の処理にプログラムは移っていきます。
繰返しの条件の設定を間違えると、1回も実行しなかったり、止まらない繰返しを作ったりすることがあります。 ここでは、いくつかの例を見て、どうしてそのようになってしまっているのか説明しましょう。
//永遠に繰り返す例(条件をいつでも満足してしまっている) var n = 1; while ( n > 0 ) { document.write( n + "<br>" ); n = n + 1; }
//1回も繰り返さない例(条件を最初から満足しない) var n = 20; while ( n < 0 ) { document.write( n + "<br>" ); n = n - 1; }
//永遠に繰り返す例(別の変数を変更している) var n = 1; var m = 1; while ( n < 20 ) { document.write( n + "<br>" ); m = m + 1; }
//永遠に繰り返す例(条件設定が甘い) var n = 9; while ( n != 0 ) { document.write( n + "<br>" ); n = n - 2; }
上の例では、ループ変数の値が9→7→5→3→1→−1→−3となっていき、結局0になりません。 熟練プログラマは、このようなことを嫌い、「n >= 0」というような形で条件を厳しく設定するようにしています。
//永遠に繰り返す例(文法的に切り離されている) var n = 1; while ( n <= 10 ) ; { // ←ここにセミコロンがはいっているので document.write( n + "<br>" ); // このブロックは切り離されてしまい、永遠に実行されてしまう n = n + 1; }
上の例のように、文法的な間違いによってブロックが切り離される場合があります。 このときも、ループ変数が更新されないので、永遠に実行されてしまいます。
繰返しのブロックの中に更に繰返し文が入った状態を、繰返し文の「ネスト(Nesting)」(あるいは「入れ子構造」)と呼びます。例えば、次の繰返し文のネストでは、外側の繰返しのループ変数はnで、内側の繰返しのループ変数はmで制御しています。
var n = 1; while ( n <= 9 ){ // var m = 1; while ( m <= 9 ){ document.write( n * m + " " ); m = m + 1; } document.write( "<br>" ); n = n + 1; }
外側の繰返しブロックと内側の繰返しブロックがありますが、 このように繰返しがネストされているとどうなるか少し考えてみましょう。 内側の繰返しの中のブロックは、計何回実行されるでしょうか?これは、足し算ではなく!!掛け算になります。 9回×9回=81回実行されます。 それから、内側のブロックの繰返しが終わった後、「document.write( "<br>" );」という記述が あるのに注目して 下さい。この部分は9回だけ実行されます。
以下の繰返しのネストでは、■が格子状に表示されます。
var n = 1; while ( n <= 7 ) { var m = 1; while ( m <= 7 ){ document.write( "■" ); m = m + 1; } document.write( "<br>" ); n = n + 1; }
■ ■■ ■■■ ■■■■ ■■■■■ ■■■■■■ ■■■■■■■
☆ ☆☆☆ ☆☆☆☆☆ ☆☆☆☆☆☆☆ ☆☆☆☆☆ ☆☆☆ ☆
繰返しを行なうwhile文のブロックの中にif文を入れた場合、ループ変数の値を参照して、毎回の繰返しで行なう処理を変えることができます。以下の例は、トランプのカードを表示しています。
var counter = 0; while ( counter < 52 ){ if ( counter < 13 ) { document.write( "♤"+(counter+1) ); } else if ( counter < 26 ) { document.write( "♡"+(counter+1) ); } else if ( counter < 39 ) { document.write( "♢"+(counter+1) ); } else { document.write( "♧"+(counter+1) ); } counter = counter + 1; if ( counter % 13 == 0 ) { document.write( "<br>" ); } }
前回if文を用いて、2の倍数や3の倍数を求めましたが、これを繰返しの中に入れて、 どの数が2の倍数、3の倍数なのかを示すことができます。
var n = 2; while ( n <= 100 ){ if ( n % 2 == 0 && n % 3 == 0 ) { document.write( n + "は2と3の公倍数です。<br>" ); } else if ( n % 2 == 0 ) { document.write( n + "は2の倍数です。<br>" ); } else if ( n % 3 == 0 ) { document.write( n + "は3の倍数です。<br>" ); } n = n + 1; }
下のプログラムは、3重にネストされた繰返しを用いています。 1から10までの範囲で、3つの数の組み合わせを表示しています。 一番内側のブロックでは、10×10×10=1000回の繰返しが行なわれます。
var p = 1; while ( p <= 10 ){ var q = 1; while ( q <= 10 ) { var r = 1; while ( r <= 10 ){ document.write( "( " + p + ", " + q + ", " + r + " )<br>" ); r = r + 1; } q = q + 1; } p = p + 1; }
ループ変数の値を使って繰返しを行なえば、様々なことが可能になってきます。たとえば、1から10まで の総和を求めるには、初期値0の変数を別に用意しておいて、それに足し込んでいきます。
var sum = 0; // 総和を求めるための変数、最初は0にしておく var n = 1; // ループ変数1〜10まで変化させる while ( n<=10 ) { sum = sum + n; // 総和にループ変数の値を足し込んでいる document.write( "1から" + n + "までの総和は、" + sum + "です。<br>" ); n = n + 1; }
これを応用して、ユーザから入力された値までの総和を求めたり、 奇数や偶数だけの総和を求めたりすることが できます。
var oddsum = 0; // 奇数の総和を求めるための変数、最初は0にしておく var evensum = 0; // 偶数の総和を求めるための変数、最初は0にしておく var m = prompt( "最終値を入力してください", 10 ); var n = 1; // ループ変数1〜mまで変化させる while ( n <= m ) { if (n % 2 == 0 ) { evensum = evensum + n; // 偶数の総和にループ変数の値を足し込む } else { oddsum = oddsum + n; // 奇数の総和にループ変数の値を足し込む } n = n + 1; } document.wirte( "1から" + m + "までの奇数の総和は" + oddsum + "で、偶数の総和は" + evensum + "です。<br>" );
ループ変数を1つずつ大きくしていきましたが、先の例のように2倍にしていったりすることもあります。 おなじように、ループ変数は1つずつ大きくしていきながらも、計算式によって、計算結果を小さくしていくための 方法があります。1つは、補数(Complement)を使う方法です。 一般に、ある数と別の数が補数関係にあるというのは、 それらの2つの数を足すと、一定の決まった値になるということです。 たとえば、1と9、2と8、3と7、4と6、5と5、 それぞれ足すと10になりますから、それぞれの組のもう一方の値を10の補数と呼びます。たとえば、2については、 8が10の補数となっています。補数関係を使って、特定の数からループ変数を引いて計算結果を小さくしていけます。 次のプログラムは、0から60までの数を3ずつの間隔で、60からの補数と共に表示していきます。
var n = 0; // ループ変数0〜20まで変化させる while ( n <= 20 ) { document.write( n * 3 + "と、その60からの補数" + (60 - n * 3) + "を足すと60になります。<br>" ); n = n + 1; }
もう1つは逆数(Reciprocal Number)を使う方法です。これは、大きな数をループ変数で割る方法です。 次のプログラムは、60を1から60まで割って、逆数を表示します。
var n = 1; // ループ変数1〜60まで変化させる while ( n <= 60 ) { document.write( "60を"+ n + "で割った結果は、" + 60/n + "になります。<br>" ); n = n + 1; }
論理式を扱うための変数をJavaでは宣言することができます。 型は論理型を用います。 論理型の変数には、真偽値の値(trueとfalse)や、論理式を評価した値を 代入することができます。 たとえば、次の記述は、論理型の変数に真偽値や、論理式を評価した結果を初期値代入しています。 xやyは整数型の変数であると仮定してください。両方とも10より大きいときは、statusに trueが代入され、そうでないときは、false が代入されます。
var condition = true; var status = x > 10 && y > 10;
この論理型の変数を用いて、if文の分岐条件や、while文の継続条件を記述することができます。
var satisfied = false; while ( !satisfied ) { value = prompt( "一桁の整数を入力して下さい: ", 3 ); if ( 0 <= value && value <= 9 ) { satisfied = true; } }
上記のif文は、単に「satisfied = (0 <= value && value <= 9); 」と記述しても構いません。