自由拡張
足す前に仕様を決める
ここまでで成績管理CLI のリスト版は一通り動くようになりました。ここからが本当の制作です。使ってみて足りないと思った機能を、自分で足します。
好きに作ってよいと言われて手が止まる人は多いはずです。理由ははっきりしていて、どこまでできれば完成なのかが決まっていない からです。書く前に決めることは 3 つあります。何を入力するか、何を出力するか、うまくいかなかったときどうするか。この 3 つが決まれば、あとは書くだけの作業になります。今回はその練習として、2 つの機能を仕様つきで用意しました。順位表と、平均以上の抽出です。
順位表はポインタの配列を作って並べる
リストは並べ替えが苦手です。ノードの next を付け替えて順序を変えることもできますが、登録順という情報が壊れてしまいます。楽なのは、ノードを指すポインタだけを集めた配列 を作り、その配列を並べ替える方法です。
struct Node **arr = malloc(count * sizeof(struct Node *));struct Node ** は「struct Node へのポインタ」を指すポインタです。中身はノードの複製ではなく番地なので、いくら並べ替えてもリスト本体の next は 1 つも変わりません。表示が終わったら配列だけを解放します。ノードは解放しません。借りた配列の持ち主はこの関数ですが、ノードの持ち主はリストのままだからです。
並べ替えは第4章の qsort を使います。要素がポインタなので、比較関数に渡ってくるのは struct Node * を指すポインタ、つまり struct Node ** です。*(struct Node **)a と 1 段はがしてからメンバを読みます。
同点の順番は自分で決める
qsort は同点のときにどちらを前に置くか保証しません。手元で正しく見えても、別の環境では入れ替わることがあります。ですから 2 番目のキーまで決めて、順序が一意になるようにします。今回はノードに登録番号 id を持たせて、点数が同じなら id の小さいほうを上にします。
借りたものと借りていないもの
この回でいちばん間違えやすいのは、順位表を出したあとの後始末です。この関数の中で malloc したのは配列 1 本だけなので、返すのも配列 1 本だけです。配列の中に入っているのはノードの番地ですが、そのノードを作ったのはこの関数ではありません。リストがまだ使っているものを勝手に解放すれば、そのあとの一覧表示で解放済みの領域を読むことになります。
その領域を確保した人が解放する という持ち主の考え方を、ここで一度はっきりさせておきます。関数を書くときは、返す義務があるのはどれかを最初に決めます。決めておけば、あとから読む人も迷いません。
平均以上は割り算しないで比べる
平均以上の学生を選ぶとき、平均を double で求めてから比べたくなりますが、割り算をすると端数が出ます。score >= (double)total / count は多くの場合正しく動きますが、両辺に count を掛けた score * count >= total なら整数のままで、端数の心配がありません。表示のときだけ %.1f で小数にします。
要件
- 1 から 4 と 0 の動きは前回と同じ。starterCode に入っているので変えない
- 5 は点数の高い順に「1位 鈴木 95点」の形で全員を出す。0人なら「学生がいません」
- 5 で同点になったときは、登録が早いほう、つまり id の小さいほうを上にする
- 5 のあとで 2 の一覧を出しても、並びは登録順のままにする
- 5 で確保した作業用の配列は、表示が終わったら解放する。ノードは解放しない
- 6 は1行目に「平均は81.7点です」を出し、続けて平均以上の学生を登録順に「鈴木 95点」の形で出す。0人なら「学生がいません」
- 終了時は全ノードを解放し、「3件解放しました」「終了します」の2行を出す
- 入力をうながす案内文は一切出さない
入出力例
main("1 田中 80
1 鈴木 95
1 佐藤 70
5
6
0
") → "田中を登録しました
鈴木を登録しました
佐藤を登録しました
1位 鈴木 95点
2位 田中 80点
3位 佐藤 70点
平均は81.7点です
鈴木 95点
3件解放しました
終了します"
main("1 田中 80
1 鈴木 80
1 佐藤 90
5
4 鈴木
5
6
2
0
") → "田中を登録しました
鈴木を登録しました
佐藤を登録しました
1位 佐藤 90点
2位 田中 80点
3位 鈴木 80点
鈴木を削除しました
1位 佐藤 90点
2位 田中 80点
平均は85.0点です
佐藤 90点
田中 80点
佐藤 90点
2件解放しました
終了します"
main("5
6
0
") → "学生がいません
学生がいません
0件解放しました
終了します"