ブレーンアシストオンライン

開発コーディングメモ~ループ関数

PHPでよく使用するループ関数

はじめに

PHPの簡易辞書シリーズです。今回はPHPでよく使用するループ関数についてまとめてみました。”PHP ループ処理” などで検索すると、いくつかの関数の使い方がでてきます。
そのいくつかあるループ関数の中で、筆者はだいたいどの処理内容でも特定のものしか使用しておりません……それで良いのか?実際どんな違いがあるのだろうと気になったので検証をしてみました。

主なループ関数の種類

・for (以前はよく使っていた)
・foreach (今はほぼこれ)
・while (ほぼ使ったことがない)

それぞれの使い方

まずはループ用の10万件のデータを用意します。(こういう時にはfor)

$data = array();
for($i=1;$i<=100000;$i++){
$data[] = $i;
}

———————————
・for
———————————
for ( $i = 0; $i < count($data); $i++) echo $data[$i];

for文は「繰り返し回数が定まっているとき」が得意です。例えばデータベースから取得する項目が「col_1」~「col_10」まであるような場合や、ランダム数値を10個出す処理(ランダム文字列パスワード生成など)、空の配列を先に宣言しておきたいときに使います。

for文の苦手なこと
回数が一定に決まらない処理が苦手です。
$data配列の数が10個なのか、20個なのか、はたまた100個なのかわからないときにはあまり向いていません。また、内部の書き方によっては無限ループが発生する場合があります。

———————————
・foreach
———————————
foreach ($data as $key => $d) print_r($d);
※$keyは配列の何番目かを取得することができる。特に$keyは使用しなくてもいい場合は、foreach ($data as $d) echo $d;とすれば良い。

foreach文は「連想配列の処理が得意」です。(絶対的な強さを誇る)PHPではデータベースからデータを取得してきたときでも、フォームからPOSTされてくるときでも、とにかく配列を目にする機会がとても多いと思います。初めはとっつきにくいものですが、foreach文を自在に操ることができると、書き方の幅が広がります。

foreach文の苦手なこと
指定回数を繰り返す必要があるときや、空の配列を指定数作りたいとき、そもそものデータが配列ではないときは使えません。また、処理を開始するときに分解したいデータが配列でない場合はNoticeが発生します。

———————————
・while
———————————
$i=0;
$data_cnt = count($data);
while($i < $data_cnt) {
echo $data[$i];
$i++;
}

while文は「指定の条件を満たすまでのループ処理」が得意です。

while文の苦手なこと
条件を満たすまで実行し続けるので、無限ループ事故を引き起こしやすいのが難点です。

それぞれの実行速度

for : 0.0041399002075195 秒(1回目)
0.0036060810089111 秒(2回目)
0.0035638809204102 秒(3回目)

foreach : 0.0017960071563721 秒(1回目)
0.0017709732055664 秒(2回目)
0.001849889755249 秒(3回目)

while : 0.0021839141845703 秒(1回目)
0.0023689270019531 秒(2回目)
0.0020339488983154 秒(3回目)

forの処理時間がかかった原因

お気づきの方はいらっしゃるかと思いますが、forではわざと処理速度が遅くなるようにしておりました。whileは条件の比較の際に事前に取得した配列のカウントを使用しています( $data_cnt = count($data); )が、forの処理では条件比較の際にカウント関数を使用しています。こちらをwhileの処理と合わせ事前にカウント数を取得するように変更すると、

$data_cnt = count($data);
for ( $i = 0; $i < $data_cnt; $i++) echo $data[$i];

事前にカウント数を取得した場合のforの実行速度

0.002068042755127 秒(1回目)
0.0020601749420166 秒(2回目)
0.0021748542785645 秒(3回目)

著しく遅かった問題が解決したと思われます。ですが、速度はforeachが一番早いという結果は変わりませんでした。

補足

今回は、処理速度の観点から検証を行っております。しかし、処理速度と同じようにメモリの使用量にも気を付けなければなりません。メモリ使用量の観点ではループ関数には【通常型】【メモリ節約型】があります。更に、foreachには【参照渡し型】というのもあります。

・foreach
【メモリ節約型】
ループ内の最後にunset($data[$key]);
【参照渡し型】
foreach ($data as &$d){

}
【メモリ節約 & 参照渡し型】
foreach ($data as &$d){

unset($d);
}

・for
【メモリ節約型】
ループ内の最後にunset($data[$i]);

・while
【メモリ節約型】
ループ内の最後にunset($data[$i]);

実はforの【メモリ節約型】が速度もメモリ使用量も優秀であったりします。更に補足をしますと、これはphpのバージョンで全く違う結果になります。【メモリ節約型】は主にphp5系のころは顕著に差異がみられましたが、php7系はほぼ差異はみられません。いつか全てのパターンで検証をし、処理内容によりどのパターンを使用すれば効率が良いかを判断できるようなまとめ記事を作成したいものです。(自分のために)

おわりに

今回は、PHPでよく使用するループ関数とその実行速度について検証をしてみました。普段から何気なく使用しているループ関数ですが、それぞれの関数で処理速度がが異なり、ちょっとしたコードの書き方に気をつけないと著しくパフォーマンスを低下させる場合があるので注意しなくてはならないと改めて感じています。もちろん、PHPだけに言える事ではないので読者の皆様方も過去、現在のコードを今一度見直してみてはいかがでしょうか。

過去の投稿はこちら

開発コーディング メモ~日付処理関数