検索
連載

PHPのfor文&怖い無限ループから脱出できるbreak/スキップできるcontinueWeb業界で働くためのPHP入門(7)(2/2 ページ)

オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載。今回は、素数判定のロジックからbreak文やcontinue文の利点と使い方を解説。for文を使ったループ処理の基本とwhile文との違い、無限ループなども併せて紹介します【PHP 7.1含め2017年の情報に合うように更新】。

Share
Tweet
LINE
Hatena
前のページへ |       

ループのスキップ

 では、リスト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("素数判定が終了しました。");
リスト6 phplesson/chap07/checkPrimeNumberBetween3And100.php

 実行結果は、最初の「2は素数です。」の表示がないことを除けば、リスト4と同じです。

 4行目が偶数のときの処理です。6行目からが偶数でないときの、これまでと同じ素数判定のループです。このやり方でも間違いはないのですが、素数判定のループ部分のインデントがさらに深くなってしまっています。むやみにインデントを深くするのは読みやすさの点で不利です。

ループをスキップできるcontinue文

 こういったケースでは、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("素数判定が終了しました。");
リスト7 phplesson/chap07/checkPrimeNumberWithContinue.php

 実行結果はリスト6と同じです。

 偶数の場合、【1】のcontinue文でその回のループは終了し、次回のループに移ります。continue文があることで、素数判定のループはelseの中に入れる必要がなくなるわけです。

continue文+整数でスキップするループの指定

 なお、continue文もbreak文と同様に整数を指定することで、どのループをスキップするか指定できます。

 break文やcontinue文はループにおいて絶対に使わなければならないわけではありません。使わずにループを書くこともできますが、複雑なループになるほど無理やりな感じになり、読みにくく、メンテナンスしづらいものになります。うまく活用してループをスッキリと記述するように心掛けましょう。

コラム「無限ループ」

 while文の条件はループ実行前に評価されるので、もし条件が最初から偽であれば、ループは一度も実行されません。

 また、次のようにするとループから抜け出せない、いわゆる無限ループになります。

while(true) {
……
}

 これは意図した無限ループですが、条件や、条件に関わる変数の扱いを間違えてしまって、条件が偽にならず無限ループのバグとなることもあります。

 一方、意図した無限ループを実際に使うこともあります。例えば、以下のように、「ループの終了条件がループ中のさまざまな箇所で複数記述し、ループは無限ループにしておき、if文とbreak文でループを終了する」というような書き方です。

while(true) {
    ……
    if (ループ終了条件1) {
        break;
    }
    ……
    if (ループ終了条件2) {
        break;
    }
    ……
    if (ループ終了条件3) {
        break;
    }
    ……
}

for文を使ったループ処理

 ここで、もう1つのループであるforを紹介しておきましょう。

ループの考え方とfor

 今まで扱ってきたwhileループをもう一度見直してみてください。すると、ループ処理には以下の3種の処理が含まれているのが分かると思います。

  • ループが始まる前の準備
  • ループを続けるための条件
  • ループが1回処理されるごとにその末尾で行う処理

 例えば、最初に行ったリスト1では、以下のようになります。

  • ループが始まる前の準備→3行目の「$i = 2」
  • ループを続けるための条件→4行目の( )内の「$i < $num」
  • ループが1回処理されるごとにその末尾で行う処理→8行目の「$i++」

 ループ処理というものは上記3点セットが頻繁に起こります。whileループの場合は、それらがバラバラの位置に記述されています。これをまとめて記述できる構文があります。それがfor文です。for文の書き方は以下の通りです。

構文 for

for(ループ開始前の処理; 繰り返しを続ける条件; ループ1回ごとの末尾で行う処理) {

 繰り返す処理

}


while文をfor文で書き換えると

 では、リスト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("素数判定が終了しました。");
リスト8 phplesson/chap07/checkPrimeNumberWithFor.php

 実行結果はリスト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文の省略」

 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プロジェクト)。


Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る