構造体のスライス
[]Item で一覧を持つ
第5章では、商品一覧を3つの並びで持っていました。演習ではそれを []Item にまとめます。本文では別の題材として、作業名・所要時間・部屋を持つ Task で、同じ考え方を確認します。
Go
titles := []string{"掃除", "洗濯"}
minutes := []int{15, 30}
rooms := []string{"居間", "洗面所"}構造体のスライスにすると、1つになります。
Go
type Task struct {
Title string
Minutes int
Room string
}
tasks := []Task{
{Title: "掃除", Minutes: 15, Room: "居間"},
{Title: "洗濯", Minutes: 30, Room: "洗面所"},
}中の Task{...} は、型名を省略できます。[]Task と書いた時点で要素の型は決まっているからです。[]Item でも同じです。
添字のずれが起こりえない
3つの並びで持っていたときの危うさが消えます。
並び3つ 長さがずれたら壊れる。どれが対応するか約束が暗黙
[]Task 1件が1つのまとまり。ずれようがない関数の引数も1つで済みます。
Go
func sumTime(tasks []Task) int // 作業一覧を1つの引数で渡せるrange で回す
上の作業一覧の所要時間を合計します。
Go
total := 0
for _, task := range tasks {
total += task.Minutes
}task.Minutes のように、要素からフィールドを取り出して集計します。
読むだけならこれで十分です。
書き換えるときの落とし穴
第5章のクイズで触れた話が、ここで実害になります。
Go
for _, task := range tasks {
task.Minutes = 0 // 元の tasks には効かない
}range が渡してくる task はコピーです。構造体はコピーされる側なので、書き換えても元の一覧には届きません。演習の Item でも同じ性質です。
添字で指定すれば効きます。
Go
for i := range tasks {
tasks[i].Minutes = 0 // 元の tasks を書き換える
}Go を書いていて最も多い詰まりどころの1つです。 値レシーバの話と根は同じで、「コピーが渡っている」ことに気づけるかどうかにかかっています。
append も同じように使える
Go
var shortTasks []Task
for _, task := range tasks {
if task.Minutes < 20 {
shortTasks = append(shortTasks, task)
}
}名前だけではなく、作業まるごとを集める形です。集めた後も時間や部屋を参照できます。演習で Item を追加するときも、名前と価格をまとめて1つの要素にしてください。
要素を丸ごと覚える
最大の所要時間を持つ作業を探すときも、フィールドで比較して、作業そのものを覚えれば名前と時間が一緒に残ります。
Go
longest := tasks[0] // tasks が空でないことを先に確認する
for _, task := range tasks {
if task.Minutes > longest.Minutes {
longest = task
}
}> なら同じ時間の作業が後から来ても更新せず、先の作業が残ります。>= なら同じ時間でも更新します。
演習では Item の追加、価格の合計、最高額の商品を更新する部分を書きます。0件処理、初期値、走査の枠、結果の表示はひな形に用意しています。
要件
- 2つの並びから
[]Itemを組み立てる appendを使って要素を足すN件 合計M円 最高はCの形を返す- 0件のときは
該当なしを返す - 最高額が同じ商品が複数ある場合は、先に出てきたほうを採用する
- 商品名と価格の並びは同じ長さで、同じ添字は同じ商品を表す