###While と forの違いについて
PHPを学んでいるのですが、
While と forの違い(使い分け)がいまいち良くわかりません。どのように使い分けると良いのかご教示頂けますと有り難いです。
どうぞ、よろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答7件
0
繰り返す回数が決まっているならfor
条件を満たすまで繰り返すならwhile
という風にしています。
投稿2016/08/30 04:10
総合スコア122
0
どちらでも書きようによっては同じように書けます。
例1
PHP
1for($i = 0; $i < 10; $i++){ 2 // 処理 3} 4 5$i = 0; 6while($i < 10){ 7 // 処理 8 $i++; 9} 10
例2
PHP
1while($flag){ 2 // 処理 3} 4 5for(; $flag; ){ 6 // 処理 7}
ただし一般的な使い分けとしては次のようになります。
for文
・ある値について範囲内で処理を行いたい場合
・ある処理を特定の回数繰り返したい場合
while文
・ある処理を特定の条件の間だけ繰り返したい場合
投稿2016/08/30 05:07
総合スコア16998
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
while文でできることはfor文でできると教わりました。
for文は、
PHP
1for(初期化処理; ループを継続する条件; 次のループの前処理) {}
で、while文は、
PHP
1while(ループを継続する条件) {}
ですよね。while文をfor文に書き直すと、
PHP
1for(; ループを継続する条件; ){}
になります。for文をwhile文に書き直すと、
PHP
1初期化処理; 2while(ループを継続する条件) { 3 : 4 (ループ中の処理;) 5 : 6 次のループの前処理 7}
になります。お互いに書き直したソースコードを参照いしていただくと、最初の2つのコードに比べてどのようなループなのかが把握しづらいと思われませんでしたか?
従いまして、使い分ける理由と致しましては、
- 「for」「while」のキーワードでどのようなループなのかわかる。
- 「ループを継続する条件」のみのループであればwhile文の方が簡潔に書ける。
- 「初期化処理」「ループを継続する条件」「次のループの前処理」があるループではfor文の方が簡潔に書ける。
になるかと思います。
余談ではありますが、プロジェクトによっては、今後のプログラム改修の柔軟性を見据えて、「ループ文は全てfor文で書くこと」と定めているところもあります(体験談)。
投稿2016/08/30 04:11
編集2016/08/30 04:13総合スコア125
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
実際現れるループのパターンはそれほど無いと私は思っています。もし、複雑なループになる場合は、アルゴリズム自体が間違っており、単純なループに分解すべきと考えるからです。それを踏まえて、どのようなループに対してどのような構文を使うべきか考えていきます。
###配列の要素を一つ一つ見る場合
foreach
を使いましょう。配列の要素を一つ一つ出力する場合は次のようになります。
PHP
1<?php 2$a = [3, 1, 4, 1, 5, 9, 2, 6]; 3foreach ($a as $v) { 4 echo "$v\n"; 5}
何番目という情報が欲しい場合もforeach
で十分です。
PHP
1<?php 2$a = [3, 1, 4, 1, 5, 9, 2, 6]; 3foreach ($a as $k => $v) { 4 echo "$k: $v\n"; 5}
配列の要素を一つ一つ見るだけであれば、for文もwhile文も必要ありません。このforeach文は他の言語でも取り入れられており(言語によっては範囲for文とも言います)、扱う配列がはっきりしておりfor文よりも意味が明確であること、カウンタを途中で増やしてしまうなどの副作用があり得ないことなど、優位点が多いです。積極的に使っていくことをお勧めします。
###数を単純にカウントする場合
range()
とforeach
を組み合わせます。1から10までを全て足す例を見てみましょう。
PHP
1<?php 2$sum = 0; 3foreach (range(1, 10) as $i) { 4 $sum += $i; 5} 6echo "$sum\n";
2ずつ増やしたいという場合でもrange(1, 10, 2)
のようにできますので、柔軟に対応できます。逆もrange(10, 1)
のようにできますので、問題ありません。
勘のいい人は上をこのように書き変える事ができる事に気付くでしょう。
PHP
1<?php 2$sum = array_reduce(range(1, 10), function($c, $i) { return $c + $i; }, 0); 3echo "$sum\n";
最初の配列にも言えることですが、array_reduce()
の他にもarray_filetr()
やarray_map()
などを駆使すれば従来ループでしたかったことがだいたいできてしまいます。この方法は関数型プログラミングで配列処理するときの考え方の一つで、C#やRubyなど多くの言語で取り入れられています。お世辞にも他言語に比べるとPHPは使いやすいとは言い難いですが、場合によっては利用価値があると私は思っています。
###条件を確認する度に状態が変わる場合
while
を使うのがいいでしょう。例えば、標準入力から一行ずつ読みとって、逆にする場合を考えます(ASCII文字しか考慮しません)。
PHP
1<?php 2while ($line = fgets(STDIN)) { 3 echo strrev(trim($line)); 4 echo "\n"; 5}
この処理には初期化処理がありません。また、条件文の実行自体がカウンタを回す役目になっているため、更新処理も必要ありません。その意味ではfor文を使うのはいささか冗長です。
###無限ループの場合
これは好みの問題ですので、for (;;) {...}
でもwhile (true) {...}
でもどちらでも良いでしょう。私はどちらかというとwhile文が好みですが、C/C++な人はwhile(1)
だと警告が出る場合があるということでfor文派が多いらしいです。
###漸化式で次々求める場合
漸化式は前の項を元にして今の項を求めるような式です。フィボナッチ数列が有名ですね。こういう場合は、再帰関数を使うととてもすっきりした形に書けます。コラッツの問題において、1になるまでの処理の回数を求める関数を作ってみましょう。
PHP
1<?php 2function collatz($n) { 3 if ($n % 2 == 0) { 4 return $n / 2; 5 } else { 6 return $n * 3 + 1; 7 } 8} 9function count_collatz($n, $c = 0) { 10 if ($n == 1) { 11 return $c; 12 } 13 return count_collatz(collatz($n), ++$c); 14} 15echo count_collatz(27);
このcount_collatz()
が再帰関数なのですが、機械的にfor文やwhile文に置き換えることができます。このような置き換えを末尾呼出し最適化といい、プログラミング言語によっては自動的にしてくれる場合があります。しかし、残念ながらPHPはしてくれません。再帰関数は深くなりすぎるとスタックを食い尽くし、プログラムが停止してしまいます。余り深くならない場合のみ用いるべきでしょう。
しかし、数学的な漸化式がはっきりしている場合は、再帰関数を使ったループの方がわかりやすい場合があります。適材適所で使い分けると良いかもしれません。
###条件が揃うまでループする場合
先ほどのコランツの問題ですが、実際に普通のループに書き換えたいと思います。
PHP
1<?php 2function collatz($n) { 3 if ($n % 2 == 0) { 4 return $n / 2; 5 } else { 6 return $n * 3 + 1; 7 } 8} 9function count_collatz($n) { 10 $c = 0; 11 while ($n != 1) { 12 $n = collatz($n); 13 ++$c; 14 } 15 return $c; 16} 17echo count_collatz(27);
このループにおいて重要なのは条件が何であるかということです。for ($c = 0; $n != 1; ++$c) {...}
とfor文の方が良いという方がいるかも知れませんが、条件が独立しているのであれば、私はwhile文を使う方がいいと思っています。$c
と$n
は独立しており、それをfor文の中に一緒に書かれてもわかりにくく、複雑になると思うからです。
独立しているのが分かりにくいなら、for (; $n != 1; $n = collatz($n))
と提案するかも知れません。しかし、更新部分が大きく、条件部分がわかりにくくなってしまっています。for文はそれほど見やすい構文では無いため、条件以外の部分を複雑にすると重要な条件部分が一目でわかりずらく、可読性が落ちると思われます。初期処理は代入一つ程度、更新部分はインクリメントかデクリメント一つ程度にとどめるべきです。
###その他
これまで上げたループ以外のパターンは思いつきませんでした。少なくとも私はそのような物を書いたことがないのだと思います。複雑な条件、複雑な初期処理や継続処理など、いろいろなパターンがあると思いますが、それらをコードにするよりも、アルゴリズムを分解し、より単純なループになるように設計し直した方が良いと思っています。
ここまでで気付いたかと思いますが、私にとってfor文はいらない子です。長くなりましたが、言いたかったことはこの一言だけだったりします…
投稿2016/08/30 13:22
総合スコア21735
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
まず、while文とfor文は互換性があるので、
どっちか一方だけでもループを実現可能です。
しかし論理的に等価でも、可読性のために使い分けたいです。
その場合ふつうは、数が決まってるならfor、
それ以外の一般的な条件による反復はwhileで、と使い分けます。
たとえば、九九を表示するループなら、
回す数が決まっているのでforだとか。
キーを押すまで無限ループする場合はwhileだとか。
あえてfor文を使うメリットは、数が決まってる場合、
何回回すのか一行で把握できることです。
しょせんシンタックスシュガーとはいえ、
多重ループになると視認性に差が出ます。
同じ千回のループでも、whileの方は無意味に複雑で、
いかにも凡ミスを誘発しそうですよね?
PHP
1for($i = 0; $i < 10; $i++){ 2 for($j = 0; $j < 10; $j++){ 3 for($k = 0; $k < 10; $k++){ 4 //process 5 } 6 } 7}
PHP
1$i = 0; 2while($i < 10){ 3 $j = 0; 4 while($j < 10){ 5 $k = 0; 6 while($k < 10){ 7 //process 8 $k++; 9 } 10 $j++; 11 } 12 $i++; 13} 14
投稿2016/08/30 11:14
総合スコア5592
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。