制御構造1(選択)

メタボ診断プログラムの開発を通じて, 選択制御(if-else,switch-case)と 型変換について理解しよう.

授業の計画も「教育プログラム」と云いますが... 前回までは本科目の前処理でした. 今回から本処理です.本格的にプログラミングらしくなります.
教科書の該当範囲:第2.3節,第3.2節

基礎知識

制御構造

制御構造とは, 一連の処理の実行順序を操る方法です. 次の3パターンが基本となっています:

アルゴロジックの プログラムの3つの制御構造 にも説明があったよね?
選択制御文
型変換

計算処理ではデータの型の変化に注意が必要となる.

自動変換は便利な場合もあるが,意外な計算結果となってしまう可能性もある. 自動変換には頼らないこと. C言語のプログラマはデータの型を常に意識すべき.

BMI について

プログラミングとは無関係ですが... BMI(body mass index;ボディマス指数)は, ヒトの肥満度を表す体格指数であり, 次式のように定義されている:

	BMI = 体重[kg] ÷  身長[m]2

なお,身長の単位は cm ではなく m であることに注意しよう.

日本肥満学会の肥満基準
状態(体型,肥満度)指標(BMI値)
低体重(痩せ型)18.5未満
普通体重18.5以上,25未満
肥満(1度)25以上,30未満
肥満(2度)30以上,35未満
肥満(3度)35以上,40未満
肥満(4度)40以上
とりあえず「BMI = 20 程度が普通体重」と憶えておこう.

本日の作業

タスク1:メタボ診断プログラムの開発

身長・体重の入力から BMI値を計算・表示するプログラム bmi を作成してみよう.

Step 0. 準備する.

まずは,ディレクトリの準備:

  
$ mkdir p-1113
$ cd p-1113  または  cd !$
今後は説明しなくても大丈夫ですよね?

テンプレートをコピーして, ソースファイル bmi.c の編集を開始:

$ cp ~/tmpl.c bmi.c
$ gedit bmi.c &
または,gedit 内でテンプレを別名保存して編集開始.
Step 1. BMI値を計算してみる.

とりあえず,BMI値を定義式の通りに計算する基本プログラムを作成してみよう:

#include <stdio.h>

int main(void)
{
	int	cm, kg;		// 身長[cm],体重[kg] を入れるよ
	double	m, bmi;		// 身長[m],BMI値 を入れるよ

	printf("BMIによるメタボ診断\n");
	while (1) {
		printf("----\n");
		printf("身長[cm] 体重[kg] > ");
		scanf("%d %d", &cm, &kg);	// 身長・体重の入力

		m = cm/100;			// 身長の単位変換
		bmi = kg/(m*m);			// BMIの計算

		printf("BMI = %.1f\n", bmi);	// BMIの表示
	}
	return (0);
}

コンパイル・実行:

$ cc bmi.c -o bmi

$ ./bmi
身長[cm] 体重[kg] > 170 60	# まあ,標準的な身長・体重ですね...
BMI = 60.0			# えっ?どんだけー?

身長[cm] 体重[kg] > 190 50	# かなりなヤセ型ですが...
BMI = 50.0			# あれれー?

[Ctrl]+[C]

このソースコードのままだと,BMI 値が明らかに異常... 原因は何か?整数同士の割り算 cm/100 がマズい. 例えば,cm = 170 の場合,cm/100 → 170/100 → 1 であり, m は 1.7 ではなく,1.0 になっている.

これを確かめるには,「m = cm/100;」の次にでも, 新しく行を追加し,「printf("m = %f\n", m);」とかしてみると吉. これは,プログラムの内部状態を調べるための printf デバッグ と云う技ね.
もちろん,原因を特定できたら,このデバッグ行は余計なので,削除をお忘れなく.
あるいは,また後で再確認したくなりそうな場合,削除ではなく, 「//」を付けてコメント化でも OK. しかし,完成時には削除をお忘れなく.
Step 2. 正しく計算してみる.

BMI の計算部分で適切に型変換するようにソースを修正しよう:

	...
	m = (double)cm / 100.0;	// 整数を実数化してから計算
	...

再度,コンパイル・実行...

...それらしい値が算出された?

Step 3. 体型を診断してみる.

if-else文を利用し,体型診断の機能を追加してみよう:

...
int main(void)
{
	...
	while (1) {
		...
		printf("BMI = %.1f\n", bmi);

		// 体型診断
		if (bmi < 18.5) {
			printf("低体重(やせ型)\n");
		} else if (bmi < 25.0) {
			printf("普通体重\n");
		} else {
			printf("肥満\n");
		}
	}
	return (0);
}

ソースを変更したら,コンパイル・実行...

...それらしい診断結果が表示された?

条件式の順序に注意しよう. 逆の順序だとどうなるか? (if ブロックと else if との順序を交換してみよう.)

また,else を付け忘れるとどうなるか? 色々と実験し,if-else の挙動を理解しよう.

Step 4. 肥満度を診断してみる.

肥満度の階級を算出し, switch-case文によって階級毎にメッセージを変えられるようにしよう:

...
int main(void)
{
	...
	int	n;	// 肥満度の階級値を入れるよ

	printf("BMIによるメタボ診断\n");
	while (1) {
		...
		if (...) {
			...
		} else if (...) {
			...
		} else {
//			printf("肥満\n");	// ← この行を,↓このように変更・増量
			// 肥満度診断
			n = (int)((bmi - 25.0)/5.0) + 1;	// 肥満度の階級化
			switch (n) {
			case 1: printf("肥満(1度)\n"); break;
			case 2: printf("肥満(2度)\n"); break;
			case 3: printf("肥満(3度)\n"); break;
			default: printf("肥満(4度)\n");
			}
		}
	}
	return (0);
}

...実行結果を確認ね.

また,break を付け忘れるとどうなるか? 等々,ありがちなミスについて,実験してみては?

なお,このプログラムの動作ならば, switch なんて使わなくても実現できそうですが...:

		...
		} else {
			n = (int)...
			if (n > 4) n = 4;
			printf("肥満(%d 度)\n", n);
		}
		...

このように短く書けますね.

一方,switch を使えば, 各 case によって異なる診断メッセージを追加できそうですね. 「がんばれ」とか「あきらめるな」とか...

余裕ある人は, 「あと何 kg 減量すれば普通体重ですよ」 (とか逆に「...増量すれば...」) みたいな処理を追加してみては? (普通体重の BMI値の上限値 25.0 とか 24.9 から 体重の上限値 b を求めてやる. 数式 b = 24.9*m*m; そして,実際の体重との差を求めれば,必要な減量幅となる.)

タスク2:丁半博打プログラムの開発

下記の基本ソースコードを元にして, 丁半博打のプログラム dice.c を完成せよ.

これが本日の課題の重要部分.

要求仕様: 2個のサイコロの目 x,y の合計 z を計算し, z が偶数の場合に「」, 奇数の場合に「」と表示する.

実行例:

$ ./dice
2,5 の 半 
4,2 の 丁 
6,2 の 丁 
...
[Ctrl]+[C]

dice.c の基本ソースコード:(main.c のコピーを元に作成)

#include <stdio.h>		// printf(), getchar() に必要
#include <stdlib.h>		// rand() に必要

int main(void)
{
	int	x, y;	// サイコロの出目(1〜6)が入るよ
	int	z;	// 出目の合計が入るよ

	while (1) {
		x = ...;	// 1〜6 の乱数
		y = ...;	// 同上
		printf("%d,%d の ", x, y);

		...		// 合計計算,丁半判定,結果表示,等

		getchar();	// [Enter]キーの入力を待つ
	}
	return (0);
}

ヒント:

上級者向けアドバイス:

本日の課題

レポートを提出せよ.

質問 Q1〜Q3 に回答し,電子メールで提出せよ.