さまざまなデータ処理プログラムを作りながら, 反復制御(for,while,do-while)について理解しよう.
なお,条件式は if 文と共通.
ソースコード記述方法:
int カウンタ変数; // 反復回数を入れるよ for (カウンタの初期化; 反復条件; カウント) { 繰り返したい処理 }
例1:カウントアップ(0 から 9 まで数え上げる)
int i; for (i = 0; i < 10; i++) { printf("%d\n", i); }
動作:
例2:カウントダウン(9 から 0 まで数え下げる)
int i; for (i = 9; i >= 0; i--) { printf("%d\n", i); }
ソースコード記述方法:
while (反復条件) { 繰り返したい処理 }
動作:最初に反復条件を計算する. もし成立していたらブロック { } 内を処理し while へ戻る.
特殊な反復条件:
ソースコード記述方法:
do { 繰り返したい処理 } while (反復条件);
動作:最初にブロック { } 内を処理し,その後で反復条件を計算する. もし成立していたら,do へ戻る.
反復回数に応じて,for/while/do-while を使い分けよう:
複数の非負整数データを合計するプログラム total.c を 3種類のループで作り分けてみよう.
まずは,ディレクトリの準備:
$ mkdir c-0524 $ cd c-0524 または cd !$
テンプレートをコピーして, ソースファイル total.c の編集を開始:
$ cp ~/tmpl.c total.c # テンプレートを利用 $ vim total.c # エディタは何でも OK(以下同様)
または,Vim 内でテンプレコピーして編集開始:
$ vim ~/tmpl.c :f total.c
#include <stdio.h> int main(void) { int total; // 合計 int x; // データ int n, i; // データの個数,カウンタ // データの個数の入力 printf("データの個数 > "); scanf("%d", &n); // 個数を入力 // データの入力,合計の計算 total = 0; // 合計を初期化 printf("%d 個の非負整数 > ", n); for (i = 0; i < n; i++) { scanf("%d", &x); // データを入力 total += x; // 合計を加算,total = total + x; } // 結果の表示 printf("合計 = %d\n", total); return (0); }
$ cc total.c -o total $ ./total データの個数 > 5 5 個の非負整数 > 1 2 3 4 5 合計 = 15
個数の入力が面倒くさいですね. データが何個あるか入力前に数えておく必要があるなんて, 非実用的だろう. このように,for ループだと, 反復回数を最初に決めておかなければならないので仕方がない. 次に,while を使って書き換えてみよう.
... int main(void) { int total; int x;/* int n, i; // データの個数の入力 printf("データの個数 > "); scanf("%d", &n); */// データの入力,合計の計算 total = 0; printf("複数個の非負整数(最後に -1)> "); x = 0; // 初回の反復条件を成立させるための措置 while (x >= 0) { scanf("%d", &x); total += x; } total -= x; // 調整(最後の x = -1 は合計すべきデータではない) // ...本来,このような帳尻合わせはよろしくない // 結果の表示 ... }
$ cc ... $ ./total 複数個の非負整数 > 1 2 3 4 5 -1 合計 = 15
データ数が多い場合を考えれば, 使い勝手は for 版よりは良くなったよね?
しかし,調整の処理について, 正しく計算するために必要ではあるが,足した値を引いており, 無駄手間となっている. データ入力と合計計算の順序を交換すれば この調整処理は不要となる.改善版:
x = 0; while (x >= 0) { total += x; scanf("%d", &x); }// total -= x;
ただし,ループの1回目に,入力するより前に,ゼロを足しており, 不自然かつ非効率だろう.
さらに,この処理手順の場合, 必ず1回以上はループ内の入力 scanf() が実行されるので, do-while の方が適切だ.
... int main(void) { ... // データの入力,合計の計算 printf("複数個の非負整数(最後に -1)> "); x = 0; do { total += x; scanf("%d", &x); } while (x >= 0); // 結果の表示 ... }
...
ソースコード的は while 版とほとんど変わりないが, 入力終了時にブロックの上方向へ戻る必要がなく, 処理手順的には,より効率的だ. しかしまだ,入力前の合計計算という不自然・非効率な処理が残っている.
反復の練習として,掛け算を足し算の反復で計算してみよう.
ソース mul.c:(total.c の Step 1 とほぼ同じ)
#include <stdio.h> int main(void) { int x, y, z; // x * y = z int i; // カウンタ printf("非負整数 x, y > "); // 問題を入力 scanf("%d %d", &x, &y); z = ...; // 積を初期化 // 反復回数は既知なので for文 for (i = 0; i < y; i++) { // y 回繰り返す z = ...; // x を足す } printf("%d * %d = %d\n", x, y, z); // 計算結果を表示 return (0); }
考え方のヒント:x*y = x + x + x + ... = (((0 + x) + x) + x) + ...
$ cc ... $ ./mul 非負整数 x, y > 12 3 12 * 3 = 36
割り算を引き算の反復で計算してみよう. ついでに剰余(余り)も計算するよ.
ソース div.c:(mul.cと類似)
#include <stdio.h> int main(void) { int x, y, z; // x / y = z int m; // 剰余 m = x % y printf("自然数 x, y > "); // 問題を入力 scanf("%d %d", &x, &y); z = ...; // 商を初期化 m = ...; // 剰余を初期化(x をコピーしておく) // 反復回数は未知(割り算の結果)だし,ゼロかもしれないので while文 while (m >= y) { // 引き算が非負である限り反復 m = ...; // x から y を引く(実際には m から引く) z++; // 引き算の回数をカウント } printf("%d / %d = %d ... %d", x, y, z, m); // 計算結果を表示 return (0); }
考え方のヒント:引き算できた回数が計算結果となる.
$ cc ... $ ./div 自然数 x, y > 23 4 23 / 4 = 5 ... 3
総和 Sn = 1 + 2 + 3 + ... + n を求めるプログラム sum.c を作成せよ.
要求仕様:非負整数 n の数値を入力,総和 Sn の数値を表示する.
実行例:
$ ./sum 非負整数 n > 3 総和 Sn = 6 $ ./sum 非負整数 n > 10 総和 Sn = 55
試験の得点データ集合から 最大値と最小値を抽出するプログラム maxmin.c を作成せよ.
要求仕様:ゼロ個以上の整数値 0〜100 を入力, 負の場合に入力を終了. 最大値および最小値を表示する.
実行例:
$ ./maxmin 得点 > 75 得点 > 85 得点 > 40 得点 > -1 最高点 = 85 最低点 = 45
小学生向け教材「九九の計算表」を表示するプログラム 99.c を作成せよ.
実行例:
$ ./99 1の段: 1 2 3 ... 9 2の段: 2 4 6 ... 18 3の段: 3 6 9 ... 27 ... 9の段: 9 18 27 ... 81
質問 Q1〜Q5 に回答し,電子メールで提出せよ.