CodeGym /コース /C# SELF /ネストされたループとその使い方

ネストされたループとその使い方

C# SELF
レベル 4 , レッスン 5
使用可能

1. はじめに

まず最初の疑問:なんでわざわざループの中にもう一個ループを入れる必要があるの?って話。実は、データやタスクって一列だけじゃなくて、テーブルとかグリッド、もっと多次元な構造になってることが多いんだ。例えば、九九表を画面に出したいとか、2次元配列を全部回りたいとか、全ての要素ペアの組み合わせを数えたいとか。こういう時はループ一個じゃ足りなくて、ループの中にループが必要になるんだよね。

プログラミングでネストされたループってのは、目覚まし時計が2個あるみたいな感じ。外側のが鳴り始めたら、その中でまたもう一個が鳴り出す。つまり、外側の「1回」の間に、内側は全部回りきる(そして外側が進むたびにまた内側が全部回る)。

いい例が「時」と「分」。時は0から23までの外側ループ、分は0から59までの内側ループ。外側が1つ進むごとに、内側は全部の値を回るってわけ。

2. ネストされたループの書き方

C#だと、ネストされたループの書き方は普通のループと全く同じ。単純にループの中にもう一個ループを書くだけ。forwhileの例を見てみよう:


for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 4; j++)
    {
        Console.Write($"{i},{j} ");
    }
    Console.WriteLine();
}
座標を出力するためのネストされた forループ

ここでは外側ループがi0から2まで)、内側ループがj0から3まで)をコントロールしてる。iの値ごとに、内側ループはj=0からj=3まで全部回る。このコードを実行すると、座標の表がきれいに出てくるよ:

0,0 0,1 0,2 0,3 
1,0 1,1 1,2 1,3 
2,0 2,1 2,2 2,3 

whileを使った同じ例も見てみよう:

int i = 0;
while (i < 3)
{
    int j = 0;
    while (j < 4)
    {
        Console.Write($"{i},{j} ");
        j++;
    }
    Console.WriteLine();
    i++;
}

ポイント:外側ループの1回ごとに、内側ループの変数(j)は毎回初期化しないと、1行しか出力されなくなるから注意!

3. ネストされたループの使い方例

例1:チェスボード(8x8)の出力

最初の課題は、黒と白のマスでできたクラシックなチェスボードを画面に出すこと(#が黒、_が白としよう)。ネストされたforループでやってみる:


for (int row = 0; row < 8; row++)
{
    for (int col = 0; col < 8; col++)
    {
        if ((row + col) % 2 == 0)
            Console.Write("_");
        else
            Console.Write("#");
    }
    Console.WriteLine();
}
チェスボードを出力するためのネストされたループ

結果:

_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_

大事なポイント:ネストしてるからこそ、各行(row)ごとに全ての列(col)を回れる。ネストがなかったら、1行か1列しか出せないよ。

例2:九九表

ネストされたループの定番!1~9の九九表を出してみよう:


for (int i = 1; i <= 9; i++)
{
    for (int j = 1; j <= 9; j++)
    {
        Console.Write($"{i * j,3} ");
    }
    Console.WriteLine();
}
九九表を出力するためのネストされたループ

${i * j,3}のフォーマットで、表がきれいに揃うようにスペースを入れてるよ。

結果:

   1   2   3   4   5   6   7   8   9
  2   4   6   8  10  12  14  16  18 
  3   6   9  12  15  18  21  24  27 
  4   8  12  16  20  24  28  32  36 
...
  9  18  27  36  45  54  63  72  81 

4. ネストされたループのコントロールと注意点

breakcontinueのネストループでの影響

ここで初心者がよくハマる!breakcontinueを内側ループで使うと、そのループだけに効く。外側ループは普通に続くからね。


for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 5; j++)
    {
        if (j == 3)
            break; // 内側ループだけ抜ける!
        Console.Write($"{i},{j} ");
    }
    Console.WriteLine();
}
breakは内側ループだけ抜ける

結果:

0,0 0,1 0,2 
1,0 1,1 1,2 
2,0 2,1 2,2 

もし2重ループを一気に抜けたい場合(例えば最初に見つかった一致で即終了したい時とか)は、フラグを使ったり、return(全部関数内でやってる場合)みたいなテクニックを使うのが普通。

5. ネストされたループの可視化

ネストされたループの実行順序って、頭の中でイメージしにくいこともあるよね。こんなフローチャートを見てみよう:

ネストされたループの可視化

表で考えると、i1から3j1から4の時、全部で何回ループが回る?

i j(各iごとに回る値) 内側ループの回数
1 1, 2, 3, 4 4
2 1, 2, 3, 4 4
3 1, 2, 3, 4 4
合計: 3 × 4 = 12

6. ネストされたループでのミスや落とし穴

よくあるミスは、内側ループの変数の初期化を間違えること。例えば、外側ループの外で宣言して、毎回リセットしないと、内側ループが全然回らなかったり、変な動きになる。

int j = 0;
for (int i = 0; i < 3; i++)
{
    while (j < 4) // あれ?jは最初のループで4になってるかも。
    {
        Console.Write($"{i},{j} ");
        j++;
    }
    Console.WriteLine();
}

この場合、ループは1回しか動かない。内側ループの変数は外側ループの中で初期化するのを忘れずに!

それから、うっかり同じ変数名で2重ループを書いちゃうと、コンパイラに怒られるよ。「その変数はもう定義されてる」ってね。


for (int i = 0; i < 3; i++)
{
    for (int i = 0; i < 3; i++)
    {
        Console.Write($"{i},{i} ");
    }
    Console.WriteLine();
}
1
アンケート/クイズ
ループ、レベル 4、レッスン 5
使用不可
ループ
ネストされたループとその使い方
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION