はもちくわ

コードについて自分なりの解釈を書いてます。

【PHP】for文の定形以外の利用法

for文について今日は書いていきます。

私のプログラミングの入口はExcelVBAだったので、それと比べると同じforを使うループでも、PHPは「なんか自由だな。。。」と思ったので、今回それについて書いていきます。

 

まず、いろいろなページでfor文はループ回数が決まっているものに使いましょうという紹介が多くされていますね。

 

記述としては

for( $i = 0 ; $i < 5 ; $i++ ){
            〜処理〜 ;
} 

意味としては 

for( カウンターの始まりカウンターの終わり増減式  ){
  〜ループ処理〜 ;
} 

みたいな感じですよね。

具体的な動きを、上記の5回繰り返しコードでみていくと、

f:id:hamochikuwa440:20210223210815p:plain

 

となります。

第1式はループ前の初期設定で実行は1回だけ、第2式で判定し処理、最後に第3式実行です。なので結果は、0回目 1回目 ・・・4回目と0〜4まで5回処理されて終了します。

 

この第3式は増減式として考えられて、ここでは$i++($i=$i+1と一緒)と1つずつ値が増えてます。使い方はほぼこれですよね。

カウントアップの値を変更したいことが稀にあるぐらいで、その場合、「式」なので、$iの値を変える式を第3式に入れればよいわけです。

なので、

◆1ずつ減らしたければ
$i--
もしくは
$i = $i -1
◆2ずつ増やしたければ
$i+=2
もしくは
$i = $i +2
◆2倍で増やしたければ
$i*=2
もしくは
$i = $i *2

のように設定すればいいです。今回、省略式と一緒に普通の式もあげましたが、式の実行結果が大事なので、別に省略して書かなくてもいいわけです。

また、アルファベットなら次のようにカウントとして使うこともできます。

f:id:hamochikuwa440:20210223223818p:plain

これを実行すると、a回目〜g回目までの7回実行されます。

ただし、日本語は無限ループに入りますので注意が必要ですよ。

 さて、このへんまでは普通の使い方で、思ったとおりという感じなのですが、ここから紹介することが、「自由だな」と思ったことです。

 

このfor文なのですが、第1式〜第3式「どれでも省略可能」で、しかも、「カンマで式を追加」できるんです。

 

まず、式の追加から見ていきます。まず、配列データを表示しながら、データを消し、最後に変数の状態を表示するコードで見てください。

f:id:hamochikuwa440:20210223231014p:plain
(上の絵のforの前のゴニョゴニョは無視してください(笑))

 第1式にカンマで区切って、count($data)が入っているのがわかるでしょうか?ここに入れても普通に動くんですよ。

そして、ループごとに$dataの数が変わっていくコードになっています。第1式にcount( )でそのデータ数を調べていますが、実行結果はしっかり6回動いていることがわかります。

これで先程説明した第1式は初期設定、ループ前の1回だけしか処理してないことがわかりますね。やっていることは次のコードのように、ループ前に宣言することと同じということです。

f:id:hamochikuwa440:20210223225734p:plain

第1式にあえて入れる必要のある書き方が後々読み返しやすいコードになるのか?

というのは、ちょっと言い切れませんが、ループに関わる変数をループの初めに入れてしまう使いみちは、可能性があるかもしれませんね。

 

また、この話とは別件ですが、このサンプルは、ループごとに配列データを消して、データ数がループ中に変化してしまっています。そのため、よく配列検索ループで使う手法の第2式に

$i < count $data )

を入ると、ループのたびに配列データ数が減るので値が減っていくのに対し、$i++でカウントアップしてしまうので、期待する動きになりません。

 PHPマニュアルには、第2式にcount()を入れるとループのたびに、count( )を呼び出すことになるので、初回の1回実行の第1式に記述するほうが、処理が早いと紹介されています。なるほど、ループ数が多いときは検討すべきなのかもしれませんね。

 

そして、もう1つ。第1式から第3式まで式を入れた場合です。

f:id:hamochikuwa440:20210223231300p:plain

ちょっと複雑ですが、実行すると

 

$iは0 $jは10
$iは1 $jは9

 

と2行で処理が止まります。内容としては、

◆$iは
 0から始まり、5になったら終わり、1つずつカウントアップ。
◆$jは
 10から始まり、8になったら終わり、1つずつカウントダウン。

の2つが同時進行するわけですが、$jは式の命令どおり、「8になったので終了」でわかるのですが、$iがまだ条件に達していません。

「どちらかが条件になったら終わり」いわゆる「or」や「||」に見えますが、マニュアルを読むと第2式は「すべて評価するが、一番最後に書いた式で判断される」そうです。そのため、上のコードの第2式を下のように反対に

$j > 8 , $i < 5

と記述すると、$jは8になってもループは抜けずに繰り返し、$iが5になったら終わります。なので先程は2回で繰り返しが終わりましたが、今回は5回繰り返しが行われることになり、そのときは$jも一緒に処理され、6まで表示されるということになります。第2式では制限されなかったということですね。

 

このように、第2式は複数式書けますが、余り意味がないように思えます。

一方、第1式、第3式は、ループ中に別の変数もカウンターのように可変したい時などは複数式が使えそうです。

 

つぎは式の省略です。初期値も、評価式も、増減式もないループ。ご想像どおり、無限ループになります。ループの中でbreakを使って止める方法です。結局、whileやdo〜whileなどと同じような使いみちになりそうですが、この第1式、第2式、第3式はどう使ってもOKで、使うか使わないかも選べるので、うまくこの特性を使えば、自由なループを設計できる可能性はあります。

 

動きは想像できるかもしれませんが少し例をあげると、

f:id:hamochikuwa440:20210223233452p:plain

このようになります。また、ここで例は挙げてませんが、実際に使っていくことを考えると、第2式の評価式だけ使い、while文のように「値が0でないならループ抜ける」みたいな書き方が可能ですし、第3式だけ使い、繰り返し回数はわからないけど、繰り返しているときだけ式を実行させる時など利用方法はあります。ただ、思いつくやり方は、他のループ使えば解決しますね、、、、

あと、全く関係ないですが、、、for( ; ; )は可愛いかもしれませんね(笑)

 

そして、番外編です。式を使ってループしてくれるfor文ですので、こんな使い方もできるという例です。

今日から5日間を表示させるループです。

$today = new DateTime();
for($day = new DateTime() ; $today->diff($day)->d< 5 ; $day = $day->modify('+1 day')){
echo $day->format('Y年m月d日'),'<br>';
}

forの「数値」を使って、テキスト表記した方が早いんですけど、、、は言わない約束ですよ(笑)あとあとオブジェクトとして使えるし!、、、と思いましたが、使うときはその時宣言すれば解決しますな(笑)

 

実は、最初にサンプル書いた時は、第2式を

$day  < new DateTime ( '+5 day' )

としていたんですが、、、、比較するたびにオブジェクトを作成するありえないものになっていました。結果も6日分表示されてしまった。これやるなら、適当な変数に5日後のオブジェクトを作って、第2式で比較しないとダメでした。その方法ならうまくいきます。今回のサンプルはdiffで日付の差分を計算して評価する方法に変えました。

 

さて、今回はfor文でした。思ったより自由ではないでしょうか?

 きっと上手に組めばfor文は面白いループになるんでしょうね!