C言語基礎|多重ループの応用

はじめに:多重ループは「組み合わせる」と一気に強くなるよ

ここまで学んだ多重ループは、for の中に for が入る形が中心でしたよね。
でも実は、繰り返し文は do / while / for の3種類があるので、好きに組み合わせて入れ子にできます。

  • 外側は「もう一回やる?」を do で回す。
  • 入力チェックは while で粘る。
  • 図形や表の出力は for の二重ループで描く。

みたいに役割分担すると、プログラムが読みやすくなって、応用もしやすくなります。

サンプルプログラム:図形を選んで何度でも表示する(多重ループの組み合わせ)

このプログラムは、次の流れです。

  1. 図形の種類を選ぶ(入力チェックつき)
  2. サイズを入力する(入力チェックつき)
  3. 二重ループで図形を描画する
  4. もう一度やるか聞く(do で繰り返す)

プロジェクト名:chap4-19-1 ソースファイル名:chap4-19-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int again;

    puts("図形を表示するミニデモを始めます。");

    do {
        int type = 0;
        int size = 0;

        /* 図形メニューの入力チェック(while) */
        while (type < 1 || type > 2) {
            puts("どの図形を表示しますか?");
            puts("  1: 九九表(1〜9)");
            puts("  2: 正方形(# で描画)");
            printf("番号を入力: ");
            scanf("%d", &type);

            if (type < 1 || type > 2) {
                puts("その番号は選べません。もう一度入力してください。");
            }
        }

        /* サイズ入力チェック(while) */
        while (size < 1) {
            printf("サイズを入力(1以上): ");
            scanf("%d", &size);
            if (size < 1) {
                puts("サイズは1以上でお願いします。");
            }
        }

        /* 図形の描画(多重ループ:for の二重ループ) */
        if (type == 1) {
            puts("九九表を表示します(固定で1〜9)。");
            for (int i = 1; i <= 9; i++) {
                for (int j = 1; j <= 9; j++) {
                    printf("%3d", i * j);
                }
                putchar('\n');
            }
        } else {
            puts("正方形を表示します。");
            for (int i = 1; i <= size; i++) {
                for (int j = 1; j <= size; j++) {
                    putchar('#');
                }
                putchar('\n');
            }
        }

        /* もう一度?(do-while で全体を繰り返す) */
        printf("続けますか?(続ける:1 / 終了:0): ");
        scanf("%d", &again);

    } while (again == 1);

    puts("おつかれさまでした。");
    return 0;
}

実行例(イメージ)

  • 図形選択を間違えると、while で聞き直し
  • size が 1 未満なら、while で聞き直し
  • 描画は for の二重ループで一気に出力

多重ループの階層(擬似フローチャート)

do (again)
  while (type が 1〜2 になるまで)
    入力してチェック

  while (size が 1以上になるまで)
    入力してチェック

  if (type == 1)
    for i = 1..9
      for j = 1..9
        掛け算を表示
  else
    for i = 1..size
      for j = 1..size
        # を表示

  again を入力
while (again == 1)

この擬似フローチャートのポイント

  • do-while がいちばん外側で、全体を「もう一回」できるようにしている。
  • 入力チェックは while で粘る(条件を満たすまで抜けない)
  • 出力処理は for の二重ループが担当(行と列を回して並べる)

多重ループで「行」と「列」を作る感覚

九九表も正方形も、考え方は同じです。

  • 外側のループ:行(縦)
  • 内側のループ:列(横)

変数 i / j の役割

変数役割
i行を進める1行目→2行目→…
j1行の中で横に進める左から右へ並べる

登場する命令(関数・文)の書式と役割

「命令が何をするか」「どう書くか」を、ここで整理しておきます。

よく使う命令(関数)と繰り返し文

種類名前書式何をする?
繰り返し文forfor (初期化; 条件; 更新) 文回数やカウンタがはっきりした繰り返しに強い
繰り返し文whilewhile (条件) 文条件を満たす間くり返す(入力チェックに便利)
繰り返し文do-whiledo 文 while (条件);とにかく1回は実行してから判定する
出力putsputs(文字列);文字列を表示して改行する
出力printfprintf(書式, 値, ...);整形して表示する(数値の桁合わせも得意)
入力scanfscanf(書式, 変数のアドレス);キーボードから読み込む
出力putcharputchar(文字);文字を1つ出力する

応用のコツ:役割ごとにループを分けると読みやすい

多重ループがごちゃっと見えるときは、次のように「担当」を分けるとスッキリします。

  • 外側:全体の繰り返し(もう一回やる?)
  • 中:入力の安全確認(入力チェック)
  • 内側:出力の本体(表・図形・一覧)

こうすると、あとから機能を追加するのもラクになります。
たとえば、図形を3種類に増やすなら、if を増やすだけで済みます。

名前と識別子:同じ名前があると困るから、区別できる名前が必要

プログラムでは、変数名や関数名など「名前」を使っていろいろ管理します。
ただ、現実の名前は同姓同名があり得ますよね。プログラムで同姓同名が起きると混乱します。

そこで、プログラムの世界では「識別子」という言葉を使います。
識別子は、他と区別できるように付ける名前のことです。

表:名前の付け方のちょいコツ

対象良い例理由
図形の種類type意味が分かる。
サイズsize何の値か即分かる。
繰り返すかagain0/1 の意味が想像しやすい。
ループ用i, j行と列の定番(短くて見やすい)

演習問題

演習4-17:見出し付き九九表(横と縦の見出し+区切り線)

横に 1〜9、縦に 1〜9 の見出しを付けた九九表を表示せよ。区切り線も入れること。

解答例

プロジェクト名:chap4-19-2 ソースファイル名:chap4-19-2.c

#include <stdio.h>

int main(void)
{
    printf("    ");
    for (int j = 1; j <= 9; j++) printf("%3d", j);
    putchar('\n');

    printf("   ");
    for (int k = 0; k < 9 * 3 + 1; k++) putchar('-');
    putchar('\n');

    for (int i = 1; i <= 9; i++) {
        printf("%2d |", i);
        for (int j = 1; j <= 9; j++) {
            printf("%3d", i * j);
        }
        putchar('\n');
    }
    return 0;
}

演習4-18:指定サイズの正方形(中が空洞の枠だけ)

一辺 n の正方形を、外枠だけ * で描け。中は空白にすること。

入力例:n=5
出力例:

*****
*   *
*   *
*   *
*****

解答例

プロジェクト名:chap4-19-3 ソースファイル名:chap4-19-3.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int n;
    puts("枠だけの正方形を作ります。");
    printf("一辺: ");
    scanf("%d", &n);

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == 1 || i == n || j == 1 || j == n) putchar('*');
            else putchar(' ');
        }
        putchar('\n');
    }
    return 0;
}

演習4-19:横長の長方形(小さい方を高さ、大きい方を幅にする)

2つの整数 a, b を読み込み、小さい方を高さ、大きい方を幅として # の長方形を描け。

解答例

プロジェクト名:chap4-19-4 ソースファイル名:chap4-19-4.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int a, b;
    int h, w;

    puts("横長の長方形を作ります。");
    printf("辺その1: ");
    scanf("%d", &a);
    printf("辺その2: ");
    scanf("%d", &b);

    if (a < b) { h = a; w = b; }
    else { h = b; w = a; }

    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) putchar('#');
        putchar('\n');
    }
    return 0;
}

演習4-20:右上が直角の直角三角形(左寄せで上が広い形)

段数 n を読み込み、右上が直角になる三角形を表示せよ。

n=5 の例:

*****
****
***
**
*

解答例

プロジェクト名:chap4-19-5 ソースファイル名:chap4-19-5.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int n;
    puts("右上が直角の三角形を作ります。");
    printf("段数: ");
    scanf("%d", &n);

    for (int i = n; i >= 1; i--) {
        for (int j = 1; j <= i; j++) putchar('*');
        putchar('\n');
    }
    return 0;
}

演習4-21:ピラミッド(段数 n、中央寄せ)

段数 n を読み込み、ピラミッドを * で表示せよ。
第 i 行目の * の数は (i - 1) * 2 + 1。

解答例

プロジェクト名:chap4-19-6 ソースファイル名:chap4-19-6.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int n;
    puts("ピラミッドを作ります。");
    printf("段数: ");
    scanf("%d", &n);

    for (int i = 1; i <= n; i++) {
        int spaces = n - i;
        int stars = (i - 1) * 2 + 1;

        for (int s = 0; s < spaces; s++) putchar(' ');
        for (int k = 0; k < stars; k++) putchar('*');
        putchar('\n');
    }
    return 0;
}

演習4-22:下向き数字ピラミッド(各行は同じ数字を並べる)

段数 n を読み込み、上から下へ短くなる数字ピラミッドを表示せよ。
上から i 行目は数字 i を並べる。並べる個数は (n - i + 1) * 2。

n=3 の例:

11 11  (空白なしで 1111 でもOK)
222
33

解答例(空白なし版)

プロジェクト名:chap4-19-7 ソースファイル名:chap4-19-7.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int n;
    puts("下向き数字ピラミッドを作ります。");
    printf("段数: ");
    scanf("%d", &n);

    for (int i = 1; i <= n; i++) {
        int count = (n - i + 1) * 2;
        for (int j = 0; j < count; j++) {
            putchar('0' + (i % 10));
        }
        putchar('\n');
    }
    return 0;
}