第 6 回 | 2024-11-08¶
- 繰り返しの回数を指定する方法を学ぶ
- 繰り返しを使って図形を描く方法を学ぶ
- プログラムを一定時間スリープさせる方法を学ぶ
1. 繰り返しの回数を指定する¶
先週までの for (;;)
は無限ループだったが、今回は繰り返しの回数を指定する方法を学ぶ。
1.1 決まった回数だけ繰り返す (1)¶
for
での繰り返しの回数をコントロールするには、for
に 3 つの設定を書く。- 3 つの設定は
(最初の設定; 続ける条件; 毎回処理が終わったときにすること)
。
for における、繰り返しの流れ
- 「最初の設定」を行う。
- 「続ける条件」を確認する。条件を満たさなかったらそこで終了する。
{ }
の中身を実行する。- 「毎回処理が終わったときにすること」を行い、2.に戻る。
このコードでの for
の動き
- 「最初の設定」
int i = 0;
で int 型の変数i
を作る。値は 0。 - 「続ける条件」を確認する。
i < 3
について、i
は 0 で条件を満たしているので続行する。 { }
の中身を実行する。std::cout << i << "\n";
によって、現在のi
の値 0 が画面に出力される。- 「毎回処理が終わったときにすること」の
++i
を実行する。i
は 0 から 1 になる。 - 「続ける条件」を確認する。
i < 3
について、i
は 1 で条件を満たしているので続行する。 { }
の中身を実行する。std::cout << i << "\n";
によって、現在のi
の値 1 が画面に出力される。- 「毎回処理が終わったときにすること」の
++i
を実行する。i
は 1 から 2 になる。 - 「続ける条件」を確認する。
i < 3
について、i
は 2 で条件を満たしているので続行する。 { }
の中身を実行する。std::cout << i << "\n";
によって、現在のi
の値 2 が画面に出力される。- 「毎回処理が終わったときにすること」の
++i
を実行する。i
は 2 から 3 になる。 - 「続ける条件」を確認する。
i < 3
について、現在のi
は 3 なので条件を満たしていない。くりかえしは終了する。
- ここでの変数
i
のように、ループの制御用に使う変数を「ループ変数」という。
1.2 決まった回数だけ繰り返す (2)¶
- 設定を工夫することで、カウントダウンするループも作れる。
1.3 「n 回繰り返す」の定石¶
- ループを
n
回実行したいときは、次のテンプレに沿ってfor
を作成するのが一般的。for (int i = 0; i < n; ++i) { ... }
1.4 n を入力する¶
- 標準入力と組み合わせることで、実行時に
n
を指定できる。
#include <iostream>
int main()
{
int n;
std::cin >> n;
for (int i = 0; i < n; ++i)
{
std::cout << "Hello!\n";
}
}
1.5 ループの中身を実行しないこともある¶
- 次のコードで
0
を入力すると、初回からfor
の「続ける条件」を満たさないため、{ }
の中身を一度も実行せずにfor
を終了する。
#include <iostream>
int main()
{
int n;
std::cin >> n;
for (int i = 0; i < n; ++i)
{
std::cout << i << "\n";
}
}
- 次のコードの
for
では、i = start;
が最初に設定されるが、start
が最初から 3 以上であるときは、初回から「続ける条件」を満たさないため、{ }
の中身を一度も実行せずにfor
を終了する。
#include <iostream>
int main()
{
int start;
std::cin >> start;
for (int i = start; i < 3; ++i)
{
std::cout << i << "\n";
}
}
1.6 カウントダウン¶
- 次のようなコードで、入力した数から 0 までをカウントダウンすることができる。
#include <iostream>
int main()
{
int start;
std::cin >> start;
for (int i = start; 0 <= i; --i)
{
std::cout << i << "\n";
}
}
1.7 ループ変数を使う (1)¶
- 通常、ループの制御に使っているループ変数(この場合
i
)を、{ }
内で=
や+=
などを使って別の値に変更してはいけない。ループの制御に影響し、ループが正しく動作しなくなるおそれがある。
ループが正しく動作しなくなる例
- 次のコードは、ループ変数
i
を{ }
内で変更しているため、本来 5 回繰り返すはずのループが 3 回しか繰り返さない。
{ }
内でループ変数を変更するのは不適切だが、計算の材料として使うだけなら問題ない。
1.8 ループ変数を使う (2)¶
- 次のようなコードは、0 から 10 までの整数の合計を計算する。
#include <iostream>
int main()
{
// 合計を計算するための変数
int sum = 0;
for (int i = 0; i <= 10; ++i)
{
sum += i;
}
std::cout << sum << "\n";
}
- 0 から 100 の合計を計算してみよう(答えは 5050)。
- 98 から 100 の合計を計算してみよう(答えは 297)。
1.9 ループ変数を使う (3)¶
- 次のコードは、
A
とB
を交互に、合わせて 10 個表示する。 - ループ変数が偶数のときは
A
を、奇数のときはB
を出力することで実現している。
#include <iostream>
int main()
{
for (int i = 0; i < 10; ++i)
{
if ((i % 2) == 0)
{
std::cout << "A";
}
else
{
std::cout << "B";
}
}
std::cout << "\n";
}
2. 繰り返しを使って図形を描く¶
for
の演習として、繰り返しを使って図形を描く方法を学ぶ。現時点までの知識では画像ファイルを作成することは難しいため、代わりに文字を縦横に並べることで図形を表現する。
横方向を X 軸、縦方向を Y 軸と考え、ループ変数の名前に x
や y
を使うことにする。
2.1 縦の線を描く¶
- 各行で
#
を出力することで、縦の線を描く。
2.2 横の線を描く¶
- 繰り返し
#
を出力することで、横の線を描く。
#include <iostream>
int main()
{
for (int x = 0; x < 8; ++x)
{
std::cout << "#";
}
std::cout << "\n";
}
2.3 長方形を描く¶
- ループは入れ子にすることができる。
- 各行で横の線を描くことで、長方形を描く。
#include <iostream>
int main()
{
for (int y = 0; y < 6; ++y)
{
for (int x = 0; x < 3; ++x)
{
std::cout << "#";
}
std::cout << "\n";
}
}
2.4 三角形を描く¶
- 1 行ごとに横線の長さを増やしていくことで、三角形を描く。
#include <iostream>
int main()
{
for (int y = 0; y < 6; ++y)
{
for (int x = 0; x <= y; ++x)
{
std::cout << "#";
}
std::cout << "\n";
}
}
2.5 枠だけの長方形を描く¶
- 最初と最後の行・列のみ
#
を出力し、それ以外は半角空白を出力することで、枠だけの長方形を描く。
#include <iostream>
int main()
{
for (int y = 0; y < 5; ++y)
{
for (int x = 0; x < 8; ++x)
{
if ((x == 0) || (x == 7) || (y == 0) || (y == 4))
{
std::cout << "#";
}
else
{
std::cout << " ";
}
}
std::cout << "\n";
}
}
2.6 市松模様を描く¶
- 「行番号と列番号の和」が偶数か奇数かに応じて
X
とO
を塗り分けて長方形を描くと、市松模様になる。
#include <iostream>
int main()
{
for (int y = 0; y < 5; ++y)
{
for (int x = 0; x < 8; ++x)
{
if ((x + y) % 2 == 0)
{
std::cout << "X";
}
else
{
std::cout << "O";
}
}
std::cout << "\n";
}
}
3. プログラムを一定時間スリープさせる¶
高性能オンラインコンパイラを使う
これ以降のコードは時間の経過を扱う。最終結果のみを表示する Simple C++ Editor や Wandbox では結果を時系列で確認できない。高性能オンラインコンパイラ「replit」を使って実行する。
3.1 スリープしない場合¶
cout
を使った出力は短時間で終わるため、次のようなコードでは、始め
と終わり
はほぼ同時に出力される。
3.2 スリープする場合¶
std::this_thread::sleep_for(duration)
でプログラムを、少なくともduration
だけスリープ(停止)させることができる。duration
は、具体的にはstd::chrono::milliseconds{ n }
を使って指定する。std::chrono::milliseconds(n)
のように( )
を使ってもよい。
- 次のコードは少なくとも 3000 ミリ秒停止する(1 秒は 1000 ミリ秒)。
#include <iostream>
#include <thread> // (1)!
#include <chrono> // (2)!
int main()
{
std::cout << "始め\n";
std::this_thread::sleep_for(std::chrono::milliseconds{ 3000 });
std::cout << "終わり\n";
}
std::this_thread::sleep_for()
を使うためのインクルードstd::chrono::milliseconds
を使うためのインクルード
3.3 カウントダウン¶
- カウントダウンする
for
とスリープを組み合わせることで、カウントダウンタイマーを実現できる。 - 3.2 で述べたように、スリープする時間は「少なくとも」であるため、厳密には 1 秒よりもわずかに長くなるが、練習なのでこの講義では気にしないでよい。
#include <iostream>
#include <thread>
#include <chrono>
int main()
{
for (int i = 10; 0 <= i; --i)
{
std::cout << i << "\n";
// 1 秒スリープ
std::this_thread::sleep_for(std::chrono::milliseconds{ 1000 });
}
std::cout << "終わり\n";
}
✅ 振り返りチェックリスト¶
- 繰り返しの回数を指定する方法を学んだ
- 繰り返しを使って図形を描く方法を学んだ
- プログラムを一定時間スリープさせる方法を学んだ
課題¶
🐣 練習問題¶
入力された秒数からカウントダウンするタイマーのプログラムを作成します。次の 2 つの機能を実装してください。
- 残り時間(秒)と、進捗を整数 % で表示する。
- カウントダウンごとに 1 秒スリープする。
カウントダウンする秒数は次の形式で標準入力から与えられます。
入力データ | 意味 |
---|---|
seconds |
カウントダウンする秒数(1 以上 99 以下の整数) |
入力例 2
解法 Step 1¶
- 入力した数から 0 までカウントダウンします。
#include <iostream>
int main()
{
int time;
std::cin >> time;
for (int i = time; 0 <= i; --i)
{
std::cout << i << "\n";
}
}
解法 Step 2¶
- 進捗が何 % であるかを計算して表示します。
#include <iostream>
int main()
{
int time;
std::cin >> time;
for (int i = time; 0 <= i; --i)
{
int progress = (100 * (time - i) / time);
std::cout << i << " [" << progress << " %]\n";
}
}
解法 Step 3¶
- カウントごとに 1 秒スリープします。
#include <iostream>
#include <thread>
#include <chrono>
int main()
{
int time;
std::cin >> time;
for (int i = time; 0 <= i; --i)
{
int progress = (100 * (time - i) / time);
std::cout << i << " [" << progress << " %]\n";
std::this_thread::sleep_for(std::chrono::milliseconds{ 1000 });
}
}
開発中はスリープをオフにする
開発中も 1 秒スリープするようにしていると、当然ながら実行結果の確認に時間がかかり、作業効率が悪い。スリープ部分をコメントにしたり、スリープ時間を短くして開発するとよい。提出の際にはその部分の修正を忘れないように。
📝 課題¶
問題文¶
指定した時間(分と秒)からカウントダウンするタイマーのプログラムを作成します。次のような機能を実装してください。
- 残り時間が 1 分 30 秒の時は
1:30
, 1 分 9 秒の時は1:09
のように、秒はつねに 2 桁で表示する。 - カウントダウンごとに進捗を 1 行のプログレスバー(形式自由)で表示する。
- カウントダウンごとに 1 秒スリープする。
カウントダウンする時間の分と秒は、次の形式で標準入力から与えられます。
入力データ | 意味 |
---|---|
minutes |
カウントダウン時間の分の部分(0 以上 9 以下の整数) |
seconds |
カウントダウン時間の秒の部分(0 以上 59 以下の整数) |
コードまたはコードの共有 URL(Wandbox)を提出してください。
ヒント 1
分と秒を分けて二重のループにするとプログラムが複雑になります。「1 分 30 秒」であれば「90 秒」のカウントダウンと考え、1 つのループを作れば、プログラムをシンプルにすることができます。分と秒は残りの秒から再計算できます。
ヒント 2
初心者は一度に全部を作ろうとして失敗します。練習問題の Step のように、まずは何を実装すべきか 1 つずつ整理しながら、段階を踏んでプログラムを作ることをお勧めします。ある程度のステップまで動くものが作れていれば、部分点を獲得できます。
入出力例¶
例 1
0:30 __________ [0 %]
0:29 __________ [3 %]
0:28 __________ [6 %]
0:27 #_________ [10 %]
0:26 #_________ [13 %]
0:25 #_________ [16 %]
0:24 ##________ [20 %]
0:23 ##________ [23 %]
0:22 ##________ [26 %]
0:21 ###_______ [30 %]
0:20 ###_______ [33 %]
0:19 ###_______ [36 %]
0:18 ####______ [40 %]
0:17 ####______ [43 %]
0:16 ####______ [46 %]
0:15 #####_____ [50 %]
0:14 #####_____ [53 %]
0:13 #####_____ [56 %]
0:12 ######____ [60 %]
0:11 ######____ [63 %]
0:10 ######____ [66 %]
0:09 #######___ [70 %]
0:08 #######___ [73 %]
0:07 #######___ [76 %]
0:06 ########__ [80 %]
0:05 ########__ [83 %]
0:04 ########__ [86 %]
0:03 #########_ [90 %]
0:02 #########_ [93 %]
0:01 #########_ [96 %]
0:00 ########## [100 %]
例 2
1:30 __________ [0 %]
1:29 __________ [1 %]
1:28 __________ [2 %]
1:27 __________ [3 %]
1:26 __________ [4 %]
1:25 __________ [5 %]
1:24 __________ [6 %]
1:23 __________ [7 %]
1:22 __________ [8 %]
1:21 #_________ [10 %]
1:20 #_________ [11 %]
1:19 #_________ [12 %]
1:18 #_________ [13 %]
1:17 #_________ [14 %]
1:16 #_________ [15 %]
1:15 #_________ [16 %]
1:14 #_________ [17 %]
1:13 #_________ [18 %]
1:12 ##________ [20 %]
1:11 ##________ [21 %]
1:10 ##________ [22 %]
1:09 ##________ [23 %]
1:08 ##________ [24 %]
1:07 ##________ [25 %]
1:06 ##________ [26 %]
1:05 ##________ [27 %]
1:04 ##________ [28 %]
1:03 ###_______ [30 %]
1:02 ###_______ [31 %]
1:01 ###_______ [32 %]
1:00 ###_______ [33 %]
0:59 ###_______ [34 %]
0:58 ###_______ [35 %]
0:57 ###_______ [36 %]
0:56 ###_______ [37 %]
0:55 ###_______ [38 %]
0:54 ####______ [40 %]
0:53 ####______ [41 %]
0:52 ####______ [42 %]
0:51 ####______ [43 %]
0:50 ####______ [44 %]
0:49 ####______ [45 %]
0:48 ####______ [46 %]
0:47 ####______ [47 %]
0:46 ####______ [48 %]
0:45 #####_____ [50 %]
0:44 #####_____ [51 %]
0:43 #####_____ [52 %]
0:42 #####_____ [53 %]
0:41 #####_____ [54 %]
0:40 #####_____ [55 %]
0:39 #####_____ [56 %]
0:38 #####_____ [57 %]
0:37 #####_____ [58 %]
0:36 ######____ [60 %]
0:35 ######____ [61 %]
0:34 ######____ [62 %]
0:33 ######____ [63 %]
0:32 ######____ [64 %]
0:31 ######____ [65 %]
0:30 ######____ [66 %]
0:29 ######____ [67 %]
0:28 ######____ [68 %]
0:27 #######___ [70 %]
0:26 #######___ [71 %]
0:25 #######___ [72 %]
0:24 #######___ [73 %]
0:23 #######___ [74 %]
0:22 #######___ [75 %]
0:21 #######___ [76 %]
0:20 #######___ [77 %]
0:19 #######___ [78 %]
0:18 ########__ [80 %]
0:17 ########__ [81 %]
0:16 ########__ [82 %]
0:15 ########__ [83 %]
0:14 ########__ [84 %]
0:13 ########__ [85 %]
0:12 ########__ [86 %]
0:11 ########__ [87 %]
0:10 ########__ [88 %]
0:09 #########_ [90 %]
0:08 #########_ [91 %]
0:07 #########_ [92 %]
0:06 #########_ [93 %]
0:05 #########_ [94 %]
0:04 #########_ [95 %]
0:03 #########_ [96 %]
0:02 #########_ [97 %]
0:01 #########_ [98 %]
0:00 ########## [100 %]