つくる リスト版成績管理
部品はそろった
この章で、追加、走査、挿入、削除、全解放を1つずつ書いてきました。今回はそれを1本の成績管理としてまとめます。作るのは、標準入力からコマンドを読んで動くプログラムです。
扱うコマンドは4つです。ADD 名前 点数 で追加、DEL 名前 で削除、LIST で一覧と合計、END で全解放して終了します。
配列版と何が変わったか
入門で作った配列版には struct Student list[10] のような上限がありました。11人目を入れようとしたら、上限を上げてコンパイルし直すか、入力を断るしかありません。リスト版に上限はありません。人数の上限は、malloc が失敗する日まで来ません。
削除の中身も変わりました。配列版は後ろ全員を1つずつ前へ詰め直していました。リスト版は矢印1本の付け替えと free 1回です。人数が増えても削除の手間は変わりません。
代わりに手放したものもあります。何番目、で一発で取り出すことはできなくなりました。この章の題材では、一覧と出し入れが中心で番号で引く場面が無いので、割の良い交換になっています。
状態は head 1本に集まる
配列版では、配列と人数の2つを常に揃えて持ち歩く必要がありました。片方だけ更新して食い違うのが定番の不具合です。リスト版で持つのは head だけで、人数は歩けば分かります。持つ状態が減ると、食い違いようがなくなります。
その代わり head は追加や削除で変わるので、これらの関数は新しい先頭を返し、呼び出し側で必ず受け直します。
head = insert_head(head, name, score);
head = delete_by_name(head, name);受け直しを1か所忘れるだけでリストが壊れるので、head を書き替える可能性のある関数は同じ形にそろえておくと間違えにくくなります。
見つからないときの扱いを決めておく
DEL で指定した名前がいないことは、異常ではなく普通に起きます。黙って何も起きないと、使う側は消えたのか消えていないのか分かりません。今回は、削除の前に探して、いなければその旨を表示することにします。LIST も同じで、1人もいないときは空行ではなく、いないことを表示します。
終わりに必ず片付ける
END では free_list を呼んで、解放した個数を表示します。個数が追加した人数と合わないなら、どこかで矢印を切ってしまい、たどり着けないノードを作ったということです。この数字は、そのままリークの検査になります。
削除の前に探す形にしておく理由
delete_by_name は、見つからないときに何もせず head を返します。呼び出し側から見ると、成功しても失敗しても戻り値は head なので、どちらだったのか区別が付きません。今回は先に find_node で探し、その結果で表示を分けます。関数を1つ増やす代わりに、削除の関数を「消す」だけの役目に保てます。成功したかどうかも返す設計にすると、戻り値が新しい先頭なのか成否なのかで悩むことになります。役目を1つに絞っておくほうが、あとから読んで迷いません。
では、コマンドを読んで振り分ける部分を書きましょう。関数はすべて用意してあります。
要件
- コマンドは ADD、DEL、LIST、END の4つを扱う
- ADD のあとには名前と点数が続く
- DEL は先に find_node で探し、いない場合は見つからない旨を表示する
- LIST は空のとき「0人です」とだけ表示する
- END では free_list の戻り値を使って個数を表示し、ループを抜ける
入出力例
main("ADD 田中 82", "ADD 鈴木 91", "ADD 佐藤 70", "LIST", "DEL 鈴木", "LIST", "END") → "田中を追加しました
鈴木を追加しました
佐藤を追加しました
佐藤 70点
鈴木 91点
田中 82点
3人 合計243点
鈴木を削除しました
佐藤 70点
田中 82点
2人 合計152点
2個解放して終了します"
main("LIST", "ADD 中村 55", "DEL 山本", "LIST", "END") → "0人です
中村を追加しました
山本は見つかりません
中村 55点
1人 合計55点
1個解放して終了します"