では、リスト4の100までの素数判定に立ち返って、少しばかり効率の点で考えてみることにしましょう。
偶数は2で割り切れますので、$numが偶数の時点で素数ではないと判定できます。これを利用すれば偶数のときは内側のwhileループを実行せずに済みます。さらに、内側のループを3から開始できます。
これをif文の条件分岐で組み込んでみましょう。$numが2のときについては偶数かつ素数なので例外的に扱わなければなりませんが、今回は説明のために簡略化し、3から開始することにします。
<?php
$num = 3;
while($num <= 100) {
if($num % 2 == 0) {
print($num."は素数ではありません<br>");
} else {
$i = 3;
while($i < $num) {
if ($num % $i == 0) {
print($num."は素数ではありません<br>");
break;
}
$i++;
}
if($i == $num) {
print($num."は素数です。<br>");
}
}
$num++;
}
print("素数判定が終了しました。");
実行結果は、最初の「2は素数です。」の表示がないことを除けば、リスト4と同じです。
4行目が偶数のときの処理です。6行目からが偶数でないときの、これまでと同じ素数判定のループです。このやり方でも間違いはないのですが、素数判定のループ部分のインデントがさらに深くなってしまっています。むやみにインデントを深くするのは読みやすさの点で不利です。
こういったケースでは、continue文を使う方がいいでしょう。continue文はループ内のそれ以降の実行をスキップし、次回のループを始めます。continue文を使って書き直すと、次のようになります。
<?php
$num = 3;
while($num <= 100) {
if($num % 2 == 0) {
print($num."は素数ではありません<br>");
$num++;
continue; // 【1】
}
$i = 3;
while($i < $num) {
if ($num % $i == 0) {
print($num."は素数ではありません<br>");
break;
}
$i++;
}
if($i == $num) {
print($num."は素数です。<br>");
}
$num++;
}
print("素数判定が終了しました。");
実行結果はリスト6と同じです。
偶数の場合、【1】のcontinue文でその回のループは終了し、次回のループに移ります。continue文があることで、素数判定のループはelseの中に入れる必要がなくなるわけです。
なお、continue文もbreak文と同様に整数を指定することで、どのループをスキップするか指定できます。
break文やcontinue文はループにおいて絶対に使わなければならないわけではありません。使わずにループを書くこともできますが、複雑なループになるほど無理やりな感じになり、読みにくく、メンテナンスしづらいものになります。うまく活用してループをスッキリと記述するように心掛けましょう。
while文の条件はループ実行前に評価されるので、もし条件が最初から偽であれば、ループは一度も実行されません。
また、次のようにするとループから抜け出せない、いわゆる無限ループになります。
while(true) {
……
}
これは意図した無限ループですが、条件や、条件に関わる変数の扱いを間違えてしまって、条件が偽にならず無限ループのバグとなることもあります。
一方、意図した無限ループを実際に使うこともあります。例えば、以下のように、「ループの終了条件がループ中のさまざまな箇所で複数記述し、ループは無限ループにしておき、if文とbreak文でループを終了する」というような書き方です。
while(true) {
……
if (ループ終了条件1) {
break;
}
……
if (ループ終了条件2) {
break;
}
……
if (ループ終了条件3) {
break;
}
……
}
ここで、もう1つのループであるforを紹介しておきましょう。
今まで扱ってきたwhileループをもう一度見直してみてください。すると、ループ処理には以下の3種の処理が含まれているのが分かると思います。
例えば、最初に行ったリスト1では、以下のようになります。
ループ処理というものは上記3点セットが頻繁に起こります。whileループの場合は、それらがバラバラの位置に記述されています。これをまとめて記述できる構文があります。それがfor文です。for文の書き方は以下の通りです。
for(ループ開始前の処理; 繰り返しを続ける条件; ループ1回ごとの末尾で行う処理) {
繰り返す処理
}
では、リスト7をforを使って書き換えてみましょう。
<?php
for($num = 3; $num <= 100; $num++) {
if($num % 2 == 0) {
print($num."は素数ではありません<br>");
continue;
}
for($i = 3; $i < $num; $i++) {
if ($num % $i == 0) {
print($num."は素数ではありません<br>");
break;
}
}
if($i == $num) {
print($num."は素数です。<br>");
}
}
print("素数判定が終了しました。");
実行結果はリスト6と同じです。
全体を通してかなり見通し良く書けるようになりました。while文では、偶数だった場合、continue文の前にインクリメントをしなければなりませんでしたが、for文ではそれもなくなります。なぜでしょうか。for文を模式化すると、次のようになります。
for(A; B; C) {
ループしたい文1;
ループしたい文2;
……
}
continue文を考えないのであれば、これは先ほどの解説通り、次のwhileループと同じです。
A;
while(B) {
ループしたい文1;
ループしたい文2;
……
C;
}
このwhileループの中でcontinue文を使った場合、「C」は実行されません。一方、for文では「continue文を使ったかどうか」にかかわらず、各ループの終了時は常に「C」を実行する点が異なります。
for文において、本文中のA、B、Cはそれぞれ省略することもできます。AとCを省略した場合は実行されないだけですが、ループ継続の条件式であるBを省略した場合、真と見なされます。つまり無限ループとなります。従って、while文による無限ループである
while(true) {
……
}
は、次のように書くこともできます。
for(;;) {
……
}
意図的に無限ループを作る場合、どちらの書き方も見受けられます。また、これまでの連載で、ifで波かっこを省略できる場合や、whileで波かっこを省略できる場合について触れてきましたが、forについても全く同様です。
ループについてはここでいったん終わりにして、次回はフォームの取り扱いについて解説します。
【2014/5/29】初版公開(山口晴広,株式会社イメージズ・アンド・ワーズ)。
【2017/6/26】PHP 7.1含め2017年の情報に合うように更新(齊藤新三/山田祥寛(監修),WINGSプロジェクト)。
WordPress活用に欠かせない、PHPをWindowsにダウンロードしてインストール、アンインストールする
PHPにおけるクラスの書き方と呼び出し方――インスタンス、メソッド、プロパティ
PHP(スクリプト言語)Copyright © ITmedia, Inc. All Rights Reserved.