質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.93%

While と forの違いについて

解決済

回答 7

投稿

  • 評価
  • クリップ 2
  • VIEW 4,125

score 12

While と forの違いについて

PHPを学んでいるのですが、
While と forの違い(使い分け)がいまいち良くわかりません。どのように使い分けると良いのかご教示頂けますと有り難いです。
どうぞ、よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 7

+4

繰り返す回数が決まっているならfor
条件を満たすまで繰り返すならwhile
という風にしています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

while文でできることはfor文でできると教わりました。

for文は、

for(初期化処理; ループを継続する条件; 次のループの前処理) {}


で、while文は、

while(ループを継続する条件) {}


ですよね。while文をfor文に書き直すと、

for(; ループを継続する条件; ){}


になります。for文をwhile文に書き直すと、

初期化処理;
while(ループを継続する条件) {
  :
  (ループ中の処理;)
  :
  次のループの前処理
}


になります。お互いに書き直したソースコードを参照いしていただくと、最初の2つのコードに比べてどのようなループなのかが把握しづらいと思われませんでしたか?

従いまして、使い分ける理由と致しましては、

  • 「for」「while」のキーワードでどのようなループなのかわかる。
  • 「ループを継続する条件」のみのループであればwhile文の方が簡潔に書ける。
  • 「初期化処理」「ループを継続する条件」「次のループの前処理」があるループではfor文の方が簡潔に書ける。

になるかと思います。

余談ではありますが、プロジェクトによっては、今後のプログラム改修の柔軟性を見据えて、「ループ文は全てfor文で書くこと」と定めているところもあります(体験談)。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

どちらでも書きようによっては同じように書けます。
例1

for($i = 0; $i < 10; $i++){
    // 処理
}

$i = 0;
while($i < 10){
    // 処理
    $i++;
}


例2

while($flag){
    // 処理
}

for(; $flag; ){
    // 処理
}

ただし一般的な使い分けとしては次のようになります。

for文
・ある値について範囲内で処理を行いたい場合
・ある処理を特定の回数繰り返したい場合

while文
・ある処理を特定の条件の間だけ繰り返したい場合

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

+2

実際現れるループのパターンはそれほど無いと私は思っています。もし、複雑なループになる場合は、アルゴリズム自体が間違っており、単純なループに分解すべきと考えるからです。それを踏まえて、どのようなループに対してどのような構文を使うべきか考えていきます。

配列の要素を一つ一つ見る場合

foreachを使いましょう。配列の要素を一つ一つ出力する場合は次のようになります。

<?php
$a = [3, 1, 4, 1, 5, 9, 2, 6];
foreach ($a as $v) {
  echo "$v\n";
}

何番目という情報が欲しい場合もforeachで十分です。

<?php
$a = [3, 1, 4, 1, 5, 9, 2, 6];
foreach ($a as $k => $v) {
  echo "$k: $v\n";
}

配列の要素を一つ一つ見るだけであれば、for文もwhile文も必要ありません。このforeach文は他の言語でも取り入れられており(言語によっては範囲for文とも言います)、扱う配列がはっきりしておりfor文よりも意味が明確であること、カウンタを途中で増やしてしまうなどの副作用があり得ないことなど、優位点が多いです。積極的に使っていくことをお勧めします。

数を単純にカウントする場合

range()foreachを組み合わせます。1から10までを全て足す例を見てみましょう。

<?php
$sum = 0;
foreach (range(1, 10) as $i) {
  $sum += $i;
}
echo "$sum\n";

2ずつ増やしたいという場合でもrange(1, 10, 2)のようにできますので、柔軟に対応できます。逆もrange(10, 1)のようにできますので、問題ありません。

勘のいい人は上をこのように書き変える事ができる事に気付くでしょう。

<?php
$sum = array_reduce(range(1, 10), function($c, $i) { return $c + $i; }, 0);
echo "$sum\n";

最初の配列にも言えることですが、array_reduce()の他にもarray_filetr()array_map()などを駆使すれば従来ループでしたかったことがだいたいできてしまいます。この方法は関数型プログラミングで配列処理するときの考え方の一つで、C#やRubyなど多くの言語で取り入れられています。お世辞にも他言語に比べるとPHPは使いやすいとは言い難いですが、場合によっては利用価値があると私は思っています。

条件を確認する度に状態が変わる場合

whileを使うのがいいでしょう。例えば、標準入力から一行ずつ読みとって、逆にする場合を考えます(ASCII文字しか考慮しません)。

<?php
while ($line = fgets(STDIN)) {
  echo strrev(trim($line));
  echo "\n";
}

この処理には初期化処理がありません。また、条件文の実行自体がカウンタを回す役目になっているため、更新処理も必要ありません。その意味ではfor文を使うのはいささか冗長です。

無限ループの場合

これは好みの問題ですので、for (;;) {...}でもwhile (true) {...}でもどちらでも良いでしょう。私はどちらかというとwhile文が好みですが、C/C++な人はwhile(1)だと警告が出る場合があるということでfor文派が多いらしいです。

漸化式で次々求める場合

漸化式は前の項を元にして今の項を求めるような式です。フィボナッチ数列が有名ですね。こういう場合は、再帰関数を使うととてもすっきりした形に書けます。コラッツの問題において、1になるまでの処理の回数を求める関数を作ってみましょう。

<?php
function collatz($n) {
  if ($n % 2 == 0) {
    return $n / 2;
  } else {
    return $n * 3 + 1;
  }
}
function count_collatz($n, $c = 0) {
  if ($n == 1) {
    return $c;
  }
  return count_collatz(collatz($n), ++$c);
}
echo count_collatz(27);

このcount_collatz()が再帰関数なのですが、機械的にfor文やwhile文に置き換えることができます。このような置き換えを末尾呼出し最適化といい、プログラミング言語によっては自動的にしてくれる場合があります。しかし、残念ながらPHPはしてくれません。再帰関数は深くなりすぎるとスタックを食い尽くし、プログラムが停止してしまいます。余り深くならない場合のみ用いるべきでしょう。

しかし、数学的な漸化式がはっきりしている場合は、再帰関数を使ったループの方がわかりやすい場合があります。適材適所で使い分けると良いかもしれません。

条件が揃うまでループする場合

先ほどのコランツの問題ですが、実際に普通のループに書き換えたいと思います。

<?php
function collatz($n) {
  if ($n % 2 == 0) {
    return $n / 2;
  } else {
    return $n * 3 + 1;
  }
}
function count_collatz($n) {
  $c = 0;
  while ($n != 1) {
    $n = collatz($n);
    ++$c;
  }
  return $c;
}
echo count_collatz(27);

このループにおいて重要なのは条件が何であるかということです。for ($c = 0; $n != 1; ++$c) {...}とfor文の方が良いという方がいるかも知れませんが、条件が独立しているのであれば、私はwhile文を使う方がいいと思っています。$c$nは独立しており、それをfor文の中に一緒に書かれてもわかりにくく、複雑になると思うからです。

独立しているのが分かりにくいなら、for (; $n != 1; $n = collatz($n))と提案するかも知れません。しかし、更新部分が大きく、条件部分がわかりにくくなってしまっています。for文はそれほど見やすい構文では無いため、条件以外の部分を複雑にすると重要な条件部分が一目でわかりずらく、可読性が落ちると思われます。初期処理は代入一つ程度、更新部分はインクリメントかデクリメント一つ程度にとどめるべきです。

その他

これまで上げたループ以外のパターンは思いつきませんでした。少なくとも私はそのような物を書いたことがないのだと思います。複雑な条件、複雑な初期処理や継続処理など、いろいろなパターンがあると思いますが、それらをコードにするよりも、アルゴリズムを分解し、より単純なループになるように設計し直した方が良いと思っています。

ここまでで気付いたかと思いますが、私にとってfor文はいらない子です。長くなりましたが、言いたかったことはこの一言だけだったりします…

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

実行回数が決まっていれば
for文

条件を満たすまで実行を継続
する場合は
while文
を使うのが定石だと思います。

for文でも無限ループなどを
作ることが可能ですが、
本来の使い方ではないと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

終了条件が状態変化である場合がwhile
例)フラグがtrueになったら、読み込みが完了したら、等
上限回数が決まっている場合はfor
私はこんな感じですね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

まず、while文とfor文は互換性があるので、
どっちか一方だけでもループを実現可能です。

しかし論理的に等価でも、可読性のために使い分けたいです。

その場合ふつうは、数が決まってるならfor、
それ以外の一般的な条件による反復はwhileで、と使い分けます。

たとえば、九九を表示するループなら、
回す数が決まっているのでforだとか。
キーを押すまで無限ループする場合はwhileだとか。


あえてfor文を使うメリットは、数が決まってる場合、
何回回すのか一行で把握できることです。

しょせんシンタックスシュガーとはいえ、
多重ループになると視認性に差が出ます。

同じ千回のループでも、whileの方は無意味に複雑で、
いかにも凡ミスを誘発しそうですよね?

for($i = 0; $i < 10; $i++){
  for($j = 0; $j < 10; $j++){
    for($k = 0; $k < 10; $k++){
      //process
    }
  }
}
$i = 0;
while($i < 10){
  $j = 0;
  while($j < 10){
    $k = 0;
    while($k < 10){
      //process
      $k++;
    } 
    $j++;
  } 
  $i++;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る