つくる 関数に分割
第4章の main を開き直す
第4章のつくる回で、人数を聞いて、その人数ぶん点数を読み、平均を出すところまで書きました。あのときは入力も計算も判定も表示も、すべて main の中に一列に並んでいました。行数にすると30行ほどですが、読むときは上から全部たどるしかありませんでした。
今回はそれを、この章で学んだ関数に切り分けます。動きは変えません。同じ入力に同じ出力を返したまま、中の並べ方だけを整えます。こういう作業を リファクタリング と呼びます。
どこで切るか
切り方の目安は、返す値があるかどうか です。
計算した結果を呼び出し元が使うなら、戻り値のある関数にします。平均を出す処理と、ランクを判定する処理がこれにあたります。画面に出して終わりなら void 関数にします。1人分の行を表示する処理がこれです。
この方針で3つの関数を用意します。
double average_of(int total, int count);
char rank_of(int score);
void show_student(int no, int score);rank_of は点数を受け取って 'A' から 'F' の1文字を返します。第2章で扱った char 型の出番です。文字リテラルはシングルクォートで囲み、表示するときの書式指定子は %c でした。
average_of の引数が点数そのものではなく合計と人数になっている点に注目してください。人数は実行するまで分かりませんし、点数を何個も引数で渡すことは今の知識ではできません。合計まではループの中で作っておき、割り算だけを関数に任せる、という分け方にします。
関数から関数を呼ぶ
show_student は表示係ですが、行の中にランクを含めます。ランクの判定基準を表示係が持ってしまうと、基準を変えたいときに直す場所が2か所になります。そこで表示係は判定を rank_of に任せます。
void show_student(int no, int score) {
printf("%d番 %d点 ランク%c\n", no, score, rank_of(score));
}関数の中から別の関数を呼ぶことに、特別な書き方は要りません。ただし呼ばれる側のプロトタイプ宣言が上にあることは必要です。
合否の判定
最後の合否は、平均が 60 以上かどうかです。これも関数にします。
int is_pass(double avg) {
return avg >= 60.0;
}第2章でやったとおり、比較の式は成り立てば 1、成り立たなければ 0 という値になります。その値をそのまま返せるので、if を書く必要はありません。
では、4つの関数に分けて組み直しましょう。main が「読んで、呼んで、出す」だけの短い形になれば成功です。
要件
- 1行目に人数を読み、続けてその人数ぶんの点数を読む
- 最初に = 成績一覧 = の行を表示する
- 1人ずつ 1番 85点 ランクB の形で表示する。表示は show_student という void 関数が行う
- ランクは rank_of という関数が char で返す。90以上がA、80以上がB、70以上がC、60以上がD、それ未満がF
- 平均は average_of という関数が合計と人数を受け取って double で返す
- 平均を 平均は82.3点です の形で小数第1位まで表示する
- 平均が60以上かどうかを is_pass という関数で判定し、判定 合格 または 判定 不合格 と表示する
入出力例
main("3", "85", "72", "90") → "=== 成績一覧 ===
1番 85点 ランクB
2番 72点 ランクC
3番 90点 ランクA
平均は82.3点です
判定 合格"
main("2", "55", "45") → "=== 成績一覧 ===
1番 55点 ランクF
2番 45点 ランクF
平均は50.0点です
判定 不合格"
main("4", "100", "60", "79", "81") → "=== 成績一覧 ===
1番 100点 ランクA
2番 60点 ランクD
3番 79点 ランクC
4番 81点 ランクB
平均は80.0点です
判定 合格"