メタボ診断プログラムの開発を通じて, 選択制御(if-else,switch-case)と 型変換について理解しよう.
制御構造とは, 一連の処理の実行順序を操る方法です. 次の3パターンが基本となっています:
「順次処理」 とか 「逐次処理」などとも云う.
「条件分岐処理」などとも云う. C言語では,if-else 文や switch-case 文を使う. 今回学習する.
「繰り返し処理」ともいう.これも「条件分岐処理」の一種でもある. C言語では,while 文,do-while 文と for 文がある. 次回学習予定.
ソースコード記述方法:
if (条件式1) { // 式1 は成立しているか? ...; // 式1 が成立の場合の処理 } else if (条件式2) { // 式1 は不成立だったが,式2 はどうか? ...; // 式2 が成立の場合の処理 } else if ... { ...; } else { // すべて不成立だったら... ...; // 不成立の場合の処理 }
条件式としては,等式 x == 0 や不等式 x < 0 などを使える. 条件式に使う比較演算子の種類については教科書 pp.49-50 を参照しよう. なお,比較や代入も計算の一種であり, 「==」や「<」も「+」や「/」と同様に 演算子と云う. また,等式の演算子が「=」ではなく「==」であることに注意しよう. 「等しくない」は「!=」.
ソースコード記述方法:
switch (整数式) { // 実数式は無理 case 値1: ...; // 式 == 値1 の場合の処理 break; case 値2: ...; // 式 == 値2 の場合の処理 break; ... default: ...; // その他の場合の処理 break; }
基本的には,それぞれの場合の区切りに break を付け忘れないように注意しよう. 最後の break だけは省略しても問題ない.
計算処理ではデータの型の変化に注意が必要となる.
123/100 → 1 整数同士なら整数(小数部は切り捨て) 123.0/100.0 → 1.23 実数同士なら実数 10/3*3 → 9 えっ,10 じゃないの?(10/3*3 = (10/3)*3 → 3*3 = 9)
123.0/100 → 123.0/100.0 → 1.23 123/100.0 → 123.0/100.0 → 1.23
ただし,変数への代入では少し事情が異なって見える. 代入も計算の一種である,と考えれば同じとも云える.
double x = 1; → double x = 1.0 実数に格上げ int y = 1.23; → double yではなく... int y = 1 小数部を切り捨て
(double)1 → 1.0 (int)1.23 → 1
自動変換は便利な場合もあるが,意外な計算結果となってしまう可能性もある. 自動変換には頼らないこと. C言語のプログラマはデータの型を常に意識すべき.
プログラミングとは無関係ですが... 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値を計算・表示するプログラム bmi を作成してみよう.
まずは,ディレクトリの準備:
$ mkdir p-1113 $ cd p-1113 または cd !$
テンプレートをコピーして, ソースファイル bmi.c の編集を開始:
$ cp ~/tmpl.c bmi.c $ gedit bmi.c &
とりあえず,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 になっている.
BMI の計算部分で適切に型変換するようにソースを修正しよう:
... m = (double)cm / 100.0; // 整数を実数化してから計算 ...
再度,コンパイル・実行...
...それらしい値が算出された?
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 の挙動を理解しよう.
肥満度の階級を算出し, 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; そして,実際の体重との差を求めれば,必要な減量幅となる.)
下記の基本ソースコードを元にして, 丁半博打のプログラム 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); }
ヒント:
乱数の範囲は,ゼロ以上,+約20億(整数型の最大値)以下となる. 範囲を変更するには,rand() に適当な演算を施せばよい. 例えば,rand()%a + b とすれば,b 以上,a+b 未満となる. (ここで,a と b は,非負整数ですよ.)
なお,乱数とは云いながら,実は, コンピュータの作る乱数は「再現性のある乱数」であり, 実行結果は毎回,同じパターンとなる. (何度も実行して確かめよう.)
割り算の余りを計算する. 例えば,式 12 % 5 の値(計算結果)は 2 となる.
質問 Q1〜Q3 に回答し,電子メールで提出せよ.