UnsplashのLewis Kang'ethe Ngugiが撮影した写真
プログラミングの反復処理で、コードが複雑になったり、意図せず無限ループになってしまったりして困ることはありませんか?この記事では、基本的な制御構文であるwhile文を、初心者にもわかりやすく解説します。while文の仕組みと注意点を押さえることで、シンプルで安全な反復処理を書けるようになります。
while文は、プログラミングにおいて特定の条件が成り立つ間、処理を繰り返し実行するための制御構文です。反復処理(ループ)を実装する際に広く使われます。ここでは、まず「構文」「条件式」「ループ内で何をするか」の基本を整理します。
while文の基本形は次のとおりです。
while (条件式) {
// 条件式が真の間、繰り返し実行される処理
}ポイントは2つです。
条件式が偽になった時点でループを抜け、次の処理に進みます。
while文の条件式には、比較演算子や論理演算子を組み合わせて「続ける条件」を書きます。たとえばJavaやJavaScriptなど、C系の構文を持つ言語では次のような演算子がよく使われます。
| 演算子 | 説明 |
|---|---|
| == | 等しい(言語によっては型変換が絡むため注意) |
| != | 等しくない |
| > | 大なり |
| < | 小なり |
| >= | 以上 |
| <= | 以下 |
| && | 論理積(AND) |
| || | 論理和(OR) |
条件式は「いつ止めるか」よりも、「いつ続けるか」を表すと考えると書きやすくなります。たとえば「入力が正しくない間、繰り返す」「ファイルの終端に達していない間、読み続ける」といった発想です。
while文の中では、目的に応じてさまざまな処理を繰り返します。代表例は次のとおりです。
注意点は「ループ内で状態を更新しないと、条件式がずっと真のままになり、無限ループに陥りやすい」ことです。ループの目的に対して、どの変数(または状態)が「終了」に向かって変化するのかを明確にしておきましょう。
while文では、ループを途中で制御するために、次のキーワードを使うことがあります(言語により名称や仕様は異なる場合があります)。
ただし、breakやcontinueが増えすぎると、処理の流れが追いにくくなります。使う場合は「どの条件で抜けるのか」「なぜスキップするのか」が読み取れるよう、条件式や分岐を整理することが大切です。
while文は「回数が決まっている反復」よりも、「条件が満たされるまで続ける反復」に向いています。ここでは、現場で出会いやすい典型的なパターンを紹介します(以下の例は主にJava風の記法です)。
while文は、ユーザーからの入力を受け付ける処理でよく使われます。特定の条件を満たす入力が得られるまで、再入力を促すことができます。
int num = 0;
while (num <= 0) {
System.out.print("正の整数を入力してください: ");
num = scanner.nextInt();
}このパターンでは、「numが正の整数になる」ことが終了条件です。入力値が更新されるため、条件式が偽になる可能性が生まれ、無限ループを避けやすくなります。
ファイルやストリームの読み込みでは、終端(EOF)まで繰り返す用途があります。終端に達するまで1行ずつ読み込むといった形です。
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();「読み込み結果がnullでない間」という条件は、終了条件が明確で、反復の目的も読み取りやすい書き方です。
配列やリストの要素を順に処理する場合も、while文で書けます。インデックスを更新しながら要素にアクセスするのが基本です。
List<String> fruits = Arrays.asList("apple", "banana", "orange");
int index = 0;
while (index < fruits.size()) {
System.out.println(fruits.get(index));
index++;
}この場合、index++が「終了に向かう更新」です。ここが抜けると、同じ要素を読み続けて無限ループになります。
while文は、実行中に状態が変化し、その結果として終了するような処理に向きます。ある値が閾値に達するまで、あるいは特定の状態になるまで処理を継続するといったケースです。
Random random = new Random();
int num = 0;
while (num < 50) {
num = random.nextInt(100);
System.out.println("Generated: " + num);
}「いつ終わるか」が乱数に左右されるため、実運用では「最大試行回数」などの安全策を設けることもあります(後述します)。
while文は強力ですが、条件や更新が少しずれるだけで、無限ループや取りこぼし、想定外の回数実行といった不具合につながります。ここでは、失敗しやすいポイントを具体的に整理します。
無限ループとは、終了条件が満たされず、処理が永遠に繰り返される状態です。プログラムが応答しなくなったり、CPU使用率が上がったり、外部システムに過剰な負荷をかけたりする原因になります。
無限ループを避けるための基本は次のとおりです。
例えば外部API呼び出しやネットワーク待ちを伴う処理では、永遠に待つことを避けるために、回数や時間で上限を持たせるのが安全です。
while文の条件式は、些細な違いで挙動が変わります。よくあるのは、境界条件のミスです。
また、言語によっては「代入」と「比較」を書き間違えると危険な場合があります。例えばC系言語では、条件式に=(代入)を書けてしまい、意図せず真扱いになることがあります。一方、Javaでは条件式に代入結果(数値など)を置けないため、その種のミスはコンパイル時に検出されます。
実務では、条件式を短くしすぎず、必要なら一度変数に分けて「何を判定しているか」を見える形にするのが安全です。
ループ内で重い処理を繰り返すと、パフォーマンスが低下します。大量データの処理、複雑な計算、I/O(ファイル・ネットワーク)を毎回行う場合は、負荷が積み上がりやすいです。
改善の方向性としては次のようなものがあります。
while文を入れ子にすると表現力は上がりますが、深いネストは可読性を下げ、バグの温床になります。ネストが深いほど、どの条件でどのループを抜けるのかが見えにくくなるためです。
ネストを抑える工夫としては次が有効です。
while文は、breakやcontinue、フラグ変数などと組み合わせることで、実務的な「止めどころ」「スキップ」「複数条件」を扱いやすくできます。ここでは、使いどころと注意点をセットで押さえます。
breakを使うと、特定の条件が成立した時点でループを強制的に終了できます。例えば「exitと入力されたら終了」のような、イベント駆動のループに使われます。
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("メッセージを入力してください(exitで終了): ");
String input = scanner.nextLine();
if (input.equals("exit")) {
break;
}
System.out.println("入力されたメッセージ: " + input);
}この書き方は直感的ですが、条件式が常にtrueなので「どこで終わるか」はbreakの条件に依存します。breakの条件が見落とされないよう、終了条件はなるべく単純に書くのが安全です。
continueを使うと、特定条件のとき、その回の残り処理をスキップして次の反復へ進めます。
int i = 0;
while (i < 10) {
if (i % 2 != 0) {
i++;
continue;
}
System.out.println(i);
i++;
}この例では、奇数のときだけ出力せずに次へ進みます。注意点は、continueの前でもi++を忘れないことです。更新が抜けると、同じ値でループし続ける原因になります。
フラグ変数(真偽値)を使うと、複数の条件を扱うループ制御を読みやすくできます。入力が妥当になるまで続ける例は典型です。
Scanner scanner = new Scanner(System.in);
boolean validInput = false;
while (!validInput) {
System.out.print("yes または no を入力してください: ");
String input = scanner.nextLine();
if (input.equals("yes") || input.equals("no")) {
validInput = true;
} else {
System.out.println("無効な入力です。もう一度入力してください。");
}
}フラグを使うと、「ループ継続の理由」が変数名に表れます。実務では、validInputのように意味が明確な名前を付けることが重要です。
while文は反復(ループ)であり、再帰(関数が自分自身を呼び出す手法)とは別物です。ただし、再帰で表現できる処理の一部は、while文で反復として書き直すことができます。たとえばフィボナッチ数列は、再帰でも書けますが、反復で書くほうがシンプルで速い場面があります。
int fibonacci(int n) {
int a = 0, b = 1, c = 0;
while (n > 0) {
c = a + b;
a = b;
b = c;
n--;
}
return a;
}このコードは「再帰」ではなく「反復(ループ)」による実装です。再帰と反復を混同しないこと、そして処理の性質に応じて「読みやすさ」「性能」「スタックの深さ」を踏まえて選ぶことが重要です。
while文は、特定の条件が成り立つ間、処理を繰り返し実行する制御構文です。ユーザー入力の受け付け、ファイルの読み込み、リストや配列の処理、特定状態になるまでの処理など、幅広い場面で活用できます。一方で、条件式や更新の設計を誤ると無限ループや回数ミスにつながります。break・continue・フラグ変数なども適切に使い、終了条件と更新を明確にしたうえで、読みやすく安全な反復処理を書くことが重要です。
使えます。条件が真の間に続けるため、回数ではなく状態で繰り返す処理に向きます。
回数やカウンタが明確ならfor文、条件が満たされるまで続けるならwhile文が適しています。
あります。while文は最初に条件を判定するため、開始時点で条件が偽なら1回も実行されません。
ループ内で条件が偽になる方向に状態が更新されているかを確認するのが最優先です。
多用すると流れが追いづらくなります。終了条件が明確になる範囲で最小限に使うのが基本です。
あります。continueの前にカウンタ更新などを入れ忘れると、同じ条件のまま繰り返してしまいます。
可能ですが、複雑すぎると誤りの原因になります。必要なら判定を変数に分けて可読性を上げるのが安全です。
終端に達するまで繰り返す形が自然で、読み込み結果がnullかどうかなど明確な終了条件を置けるためです。
ループ外へ出せる処理がないか、同じI/Oや計算を毎回行っていないかを優先して確認します。
同じではありません。while文は反復、再帰は関数呼び出しで繰り返す手法で、用途と特性が異なります。