C#: ループ構造
1. whileループ
while ループは、その条件が true である限り、コードブロックを繰り返します。条件は各反復の前にチェックされ、false である場合は、ループ本体全体がスキップされます。
int count = 1;
while (count <= 5)
{
Console.WriteLine($"Execution #{count}");
count++;
}
Execution #1
Execution #2
Execution #3
Execution #4
Execution #5
ヒント:条件が最初から
falseの場合、ループ本体は決して実行されません。
2. do...while ループ
do...while ループは、まず本体を実行し、その後、条件をチェックします。条件の結果にかかわらず、ループ本体は少なくとも 1 回は実行されます。
int number;
do
{
Console.WriteLine("Enter a positive number:");
number = int.Parse(Console.ReadLine());
} while (number <= 0);
Console.WriteLine($"You entered: {number}");
警告:
do...whileの末尾にあるセミコロンは必須です。これはwhileループとは構文が異なります。
3. forループ
for ループは、初期化、条件判定、反復処理の更新を1行にまとめることができるため、反復回数があらかじめわかっている場合に最適です。
for (int i = 1; i <= 5; i++)
{
Console.WriteLine($"i = {i}");
}
i = 1
i = 2
i = 3
i = 4
i = 5
for ステートメントの 3 つの部分はいずれも省略できますが、セミコロンは残しておく必要があります:
int j = 0;
for (; j < 3; )
{
Console.WriteLine($"j = {j}");
j++;
}
j = 0
j = 1
j = 2
4. foreach ループ
foreach ループは、コレクション内の各要素を反復処理するための C# の機能です。この機能は、簡潔で安全な構文を提供し、配列、リスト、および IEnumerable を実装するあらゆるコレクションで動作します。
string[] fruits = { "Apple", "Banana", "Orange" };
foreach (var fruit in fruits)
{
Console.WriteLine($"Fruit: {fruit}");
}
Fruit: Apple
Fruit: Banana
Fruit: Orange
警告:
foreachループは読み取り専用です。反復変数は読み取り専用のコピーです。これを通じてコレクションの要素を変更したり、反復中に要素を追加・削除したりすることはできません。そうすると、InvalidOperationException例外が発生します。
▶ サンプル
int[] scores = { 90, 85, 72, 60, 95 };
int sum = 0;
foreach (int score in scores)
{
sum += score;
}
Console.WriteLine($"Total: {sum}, Average: {(double)sum / scores.Length:F1}");
Total: 402, Average: 80.4
5. ネストされたループ
ループの中に別のループがあるものを「ネストされたループ」と呼び、行列や表などの2次元データ構造の処理によく用いられます。
for (int row = 1; row <= 3; row++)
{
for (int col = 1; col <= 4; col++)
{
Console.Write($"{row * col,4}");
}
Console.WriteLine();
}
1 2 3 4
2 4 6 8
3 6 9 12
警告:ネストされたループの時間計算量は O(n^2) です。ネストの層が深くなるほど、パフォーマンスは低下します。実際には、深いネストを避け、LINQ の使用やメソッドの抽出による最適化を検討してください。
6. break と continue
(1) 改行
break は、現在のループを直ちに終了し、残りのすべての反復をスキップします。
for (int i = 0; i < 10; i++)
{
if (i == 5) break;
Console.Write($"{i} ");
}
0 1 2 3 4
(2) 続き
continue は、現在の反復処理の残りの部分をスキップして、次の反復処理にジャンプします。
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0) continue;
Console.Write($"{i} ");
}
1 3 5
ヒント:
breakおよびcontinueは、最も内側のループにのみ影響します。複数のループ階層から抜け出すには、フラグ変数またはラベル付き文を使用してください。
7. 一般的なループパターン
(1) 数え方
条件を満たす要素を数える:
int[] data = { 12, 25, 8, 33, 17, 6, 41 };
int count = 0;
foreach (int val in data)
{
if (val > 20) count++;
}
Console.WriteLine($"Count of values > 20: {count}");
Count of values > 20: 3
(2) 蓄積パターン
一連の数の和を求めます:
int sum = 0;
for (int i = 1; i <= 100; i++)
{
sum += i;
}
Console.WriteLine($"Sum of 1 to 100: {sum}");
Sum of 1 to 100: 5050
(3) 検索パターン
コレクション内の対象要素を検索する:
int[] numbers = { 3, 7, 15, 22, 9, 31 };
int target = 22;
int index = -1;
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] == target)
{
index = i;
break;
}
}
Console.WriteLine(index >= 0 ? $"Found {target} at index {index}" : "Not found");
Found 22 at index 3
▶ サンプル
複数のパターンを組み合わせる - 成績分布を集計し、平均を算出する:
int[] scores = { 92, 78, 55, 88, 43, 96, 67, 73, 81, 50 };
int excellent = 0, pass = 0, fail = 0, total = 0;
foreach (int s in scores)
{
total += s;
if (s >= 90) excellent++;
else if (s >= 60) pass++;
else fail++;
}
double avg = (double)total / scores.Length;
Console.WriteLine($"Excellent: {excellent}, Pass: {pass}, Fail: {fail}");
Console.WriteLine($"Average: {avg:F1}");
Excellent: 2, Pass: 5, Fail: 3
Average: 72.3
8. 無限ループとその回避方法
ループ条件が常にtrueである場合、無限ループが発生し、ループが終了しなくなります。一般的な無限ループのパターン:
while (true)
{
}
for (; ; )
{
}
▶ サンプル
無限ループを回避するための終了条件を備えた制御ループ:
Random rnd = new Random();
int attempts = 0;
while (true)
{
int value = rnd.Next(1, 101);
attempts++;
if (value == 42)
{
Console.WriteLine($"Got 42 on attempt #{attempts}!");
break;
}
if (attempts > 1000)
{
Console.WriteLine("Too many attempts, exiting loop.");
break;
}
}
無限ループを回避するためのガイドライン:
whileおよびforの状態が、最終的にfalseになることを確認するwhile (true)またはfor (; ; )を使用する場合は、必ずbreakの終了パスを指定してください- 安全策として、最大反復回数を設定する
- ループ変数の更新ロジックを再確認し、増分や減分を誤ってスキップしてしまうのを防ぐ
実際には、
while (true)とbreakを組み合わせたループパターンは一般的ですが、各分岐に必ず出口があることを確認してください。
❓ よくある質問
while と do...while の違いは何ですか?while はまず条件をチェックし、本体を実行しない場合もあります。一方、do...while はまず本体を実行するため、少なくとも1回は実行されます。foreach ループ内でコレクションの要素を変更することはできますか?for ループを使用してください。break を使って、ネストされた複数のループから抜け出すことはできますか?break は最も内側のループからのみ抜け出します。複数のループから抜け出すには、フラグ変数を使用するか、内側のループをメソッドとして抽出し、return を使用してください。for (; ; ) と while (true) には違いがありますか?while (true) の方が可読性が高く、for (; ; ) は一部の環境ではコンパイラの警告を回避できる場合があります。📖 まとめ
whileはまず条件を確認し、その条件がtrueの場合にのみ本体が実行されるdo...whileは、条件を確認する前に本体を一度実行するforは、反復回数が決まっている場合に、簡潔な構文で利用するのに最適ですforeachは、コレクションをきれいに反復処理するための C# の機能ですが、反復変数は読み取り専用です- 入れ子ループは多次元データを処理できるが、O(n^2)というパフォーマンスへの影響に注意が必要である
breakはループを直ちに終了し、continueは現在の反復をスキップします- 一般的なパターン:カウント、累積、検索、探索
- 無限ループは
while (true)+breakで制御できますが、必ず終了条件を設定してください
📝 練習問題
whileループを使用して、値が1000を超えるまで2の累乗を計算し、すべての結果を出力するプログラムを作成してください。forループを使用して、上三角行列形式の九九を印刷してください(重複を避けてください)。- 文字列の配数が与えられたとき、
foreachを使用して、長さが 5 より大きい文字列がいくつあるかを数えなさい。 do...whileを使用して、正しいパスワードが入力されるまでユーザーにパスワードを繰り返し入力させるプログラムを作成してください(正しいパスワードは自分で決めてください)。- 入れ子になった
forループを使用して、1 から 50 までのすべての素数を求めなさい。 forループ内でcontinueを使用し、1から20までのうち、3で割り切れないすべての数を表示してください。