多重ループ(ネストループ)とは?
「多重ループ(ネストループ)」とは、繰り返し処理(for文やwhile文など)の内部で、さらに別の繰り返し処理を行う構造のことです。
ループが二重になっている場合を「二重ループ」、三重になっている場合を「三重ループ」と呼びます。
この構造の最大の特徴は、「外側のループ」が1回実行されるたびに、「内側のループ」が最初から最後まで全て実行される点です。
この特性は、二次元の表(行列)やグリッド(格子状のデータ)を処理する際に非常に役立ちます。
基本構文(for文のネスト)
for文を二重にする場合の基本的な構文は以下のようになります。
// 外側のループ
for (int i = 0; i < 5; i++) {
// ... 外側のループの処理 (A) ...
// 内側のループ
for (int j = 0; j < 10; j++) {
// ... 内側のループの処理 (B) ...
// この処理 (B) は 5 x 10 = 50回 実行される
}
// ... 外側のループの処理 (C) ...
// この処理 (C) は 5回 実行される
}
サンプルコード1:指定サイズの長方形を描画する
多重ループの最も分かりやすい例は、縦(行)と横(列)の二次元的な描画です。
この例では、ユーザーが指定した「高さ」と「幅」に基づいて、記号(#)を使った長方形を描画します。
- 外側のループ(縦方向): 高さを制御し、行の改行を担当します。
- 内側のループ(横方向): 幅を制御し、1行分の記号の出力を担当します。
#include <iostream>
#include <limits> // 入力検証用
int main() {
int height = 0; // 高さ
int width = 0; // 幅
// 高さを正しく入力するまでループ (do-while)
do {
std::cout << "長方形の高さを入力 (1以上の整数): ";
if (!(std::cin >> height) || height <= 0) {
std::cout << "エラー: 1以上の整数を入力してください。\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
height = 0; // ループ継続
}
} while (height <= 0);
// 幅を正しく入力するまでループ (do-while)
do {
std::cout << "長方形の幅を入力 (1以上の整数): ";
if (!(std::cin >> width) || width <= 0) {
std::cout << "エラー: 1以上の整数を入力してください。\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
width = 0; // ループ継続
}
} while (width <= 0);
std::cout << "--- 描画開始 ---\n";
// 外側のループ (縦方向の繰り返し: h は 0 から height-1 まで)
for (int h = 0; h < height; ++h) {
// 内側のループ (横方向の繰り返し: w は 0 から width-1 まで)
for (int w = 0; w < width; ++w) {
// 記号を出力 (改行しない)
std::cout << "#";
}
// 内側のループが終了 (1行分が完了) したら、改行する
std::cout << "\n";
}
std::cout << "--- 描画終了 ---\n";
return 0;
}
実行例 (高さ3, 幅5 の場合):
--- 描画開始 ---
#####
#####
#####
--- 描画終了 ---
サンプルコード2:座標グリッドと <iomanip> の setw
setwは、C++の標準ライブラリ<iomanip>(アイ・オー・マニピュレータ)に含まれる「操作子(マニピュレータ)」です。
setwは “Set Width”(幅を設定)の略で、次に出力するデータが最低でも指定した文字数分の幅を持つように設定します。データが指定幅より短い場合は、自動的に空白文字(スペース)が挿入されます。(デフォルトでは右揃え)
このsetwを多重ループと組み合わせることで、整然とした表を作成できます。
以下の例では、3行4列の座標グリッド (x, y) を、setwを使って幅を揃えて出力します。
#include <iostream>
#include <iomanip> // std::setw, std::right を使うために必要
#include <string> // std::to_string を使うために必要
int main() {
const int ROWS = 3; // 行数 (Y座標)
const int COLS = 4; // 列数 (X座標)
const int CELL_WIDTH = 10; // 各セルの幅 (10文字分)
std::cout << "座標グリッドの表示を開始します (幅: " << CELL_WIDTH << ")\n";
// 外側のループ (Y座標: 0 から ROWS-1 まで)
for (int y = 0; y < ROWS; ++y) {
// 内側のループ (X座標: 0 から COLS-1 まで)
for (int x = 0; x < COLS; ++x) {
// 表示する文字列 "(x,y)" を作成
std::string coord = "(" + std::to_string(x) + "," + std::to_string(y) + ")";
// std::setw(CELL_WIDTH) で次の出力幅を10文字に設定
// std::right で右揃えを明示
std::cout << std::right << std::setw(CELL_WIDTH) << coord;
}
// 1行の出力が終わったら改行
std::cout << "\n";
}
std::cout << "表示が完了しました。\n";
return 0;
}
実行結果:
座標グリッドの表示を開始します (幅: 10)
(0,0) (1,0) (2,0) (3,0)
(0,1) (1,1) (2,1) (3,1)
(0,2) (1,2) (2,2) (3,2)
表示が完了しました。
setwの効果により、各座標が指定した幅(10文字)の中で整然と並んでいることがわかります。
まとめ
多重ループは、C++で二次元的なデータを扱う際の基本的なテクニックです。
- 外側のループが「行」(縦方向)の制御。
- 内側のループが「列」(横方向)の制御。
この関係性を理解することが重要です。setwのようなフォーマット操作子と組み合わせることで、単なる繰り返し処理に留まらず、整った形式でのデータ出力が可能になります。
