22. Slices — Homework solutions

The .go solution files are in exercises/22/homework/solutions/.

Problem 1 — Favourite games

Problem. Print a numbered list using range.

How to think about it. range gives you the position (starting at 0) and the value. Build each line from the position plus 1, a dot, a space and the value.

Worked solution.

package main

import "fmt"

func main() {
    games := []string{"Minecraft", "Terraria", "Stardew Valley", "Hades", "Tetris"}

    for i, name := range games {
        fmt.Printf("%d. %s\n", i+1, name)
    }
}

Output:

1. Minecraft
2. Terraria
3. Stardew Valley
4. Hades
5. Tetris

Common mistakes.

  • Printing i and getting 0. Minecraft. Print i+1.
  • Using for i := 0; i < len(games); i++ and then games[i]. That works, but range is more direct.

Problem 2 — Sum the slice

Problem. Add every number in a slice, and print the total.

How to think about it. The accumulate pattern from Chapter 20: keep a running total, updated inside the loop. The position is not needed, so it is _.

Worked solution.

package main

import "fmt"

func main() {
    scores := []int{12, 7, 24, 9, 30}

    total := 0
    for _, score := range scores {
        total += score
    }

    fmt.Println("Total:", total)
}

Output:

Total: 82

Common mistakes.

  • Forgetting total := 0 before the loop. Go stops with undefined: total.

Problem 3 — Insert and remove

Problem. Do four slice operations, printing the slice after each.

How to think about it. Write a small printing function once, and call it after each step. Each operation is then one line. The slice is passed in as an argument, and %-18s from Chapter 14 lines up the labels.

Worked solution.

package main

import "fmt"

func show(label string, items []string) {
    fmt.Printf("%-18s %v\n", label, items)
}

func main() {
    items := []string{"apple", "banana", "cherry"}

    show("Start", items)

    items = append(items, "date")
    show("After append", items)

    items = append([]string{"apricot"}, items...)
    show("After insert at 0", items)

    items = items[:len(items)-1]
    show("After remove last", items)

    items = append(items[:1], items[2:]...)
    show("After remove at 1", items)
}

Output:

Start              [apple banana cherry]
After append       [apple banana cherry date]
After insert at 0  [apricot apple banana cherry date]
After remove last  [apricot apple banana cherry]
After remove at 1  [apricot banana cherry]

Common mistakes.

  • Writing append(items, "date") on its own line. Go stops with append(items, "date") (value of type []string) is not used. Assign the result back.
  • Writing items[1] = "" instead of removing the position. That leaves an empty string in the slice.
  • Mixing up the positions in append(items[:1], items[2:]...). The first part stops before position 1, and the second part starts after it.

Challenge — Reverse a slice

Problem. reverse returns a new slice, and the original is unchanged.

How to think about it. Build a new slice inside the function. Walk the original from last to first with a three-part for, append each value to the new slice, and return it. An empty slice needs no special case, because the loop body never runs.

Worked solution.

package main

import "fmt"

func reverse(items []string) []string {
    result := []string{}
    for i := len(items) - 1; i >= 0; i-- {
        result = append(result, items[i])
    }
    return result
}

func main() {
    fruits := []string{"apple", "banana", "cherry"}
    fmt.Println(fruits) // [apple banana cherry]

    backwards := reverse(fruits)
    fmt.Println(backwards) // [cherry banana apple]
    fmt.Println(fruits)    // [apple banana cherry]

    fmt.Println(reverse([]string{})) // []
}

Chapter 24 shows slices.Reverse, which does this job in the standard library. The loop above is for understanding how it works.

Common mistakes.

  • Changing items directly instead of building a new slice. A function shares its caller's items, so the original changes too, and the problem says it must not:
func reverseWrong(items []string) []string {
    for i := 0; i < len(items)/2; i++ {
        last := len(items) - 1 - i
        items[i], items[last] = items[last], items[i]
    }
    return items
}

func main() {
    fruits := []string{"apple", "banana", "cherry"}
    backwards := reverseWrong(fruits)
    fmt.Println(backwards) // [cherry banana apple]
    fmt.Println(fruits)    // [cherry banana apple]
}
  • Starting the loop at len(items). That position does not exist, and the program crashes with panic: runtime error: index out of range [3] with length 3. Start at len(items) - 1, and keep going while i >= 0.

Done?

The next chapter teaches maps, which look up a value by a name instead of by a position.