append: срез растёт
Срез из []int{12, 40, 5} знает свои элементы заранее. А остатки приходят во вводе, по одной строке, и сколько их — неизвестно. Значит, срез должен начинаться пустым и расти по мере чтения.
Пустой срез и append
Ситуация. Принять три остатка по очереди.
var cells []int
fmt.Println(cells, len(cells))
cells = append(cells, 12)
cells = append(cells, 40, 5)
fmt.Println(cells, len(cells))Что увидите.
[] 0
[12 40 5] 3Как это читать.
| Строка | Что происходит |
|---|---|
var cells []int |
срез без элементов: печатается как [], длина 0 |
cells = append(cells, 12) |
append берёт срез cells, дописывает 12 в конец и возвращает срез с новым элементом; результат кладётся обратно в cells |
append(cells, 40, 5) |
дописать можно сразу несколько значений, по порядку |
Вместо var cells []int можно написать cells := []int{}: оба среза пустые, печатаются как [], и append работает с обоими.
Картинка одного шага:
до: cells ──► [ 12 ] len 1
append(cells, 40, 5) ──► [ 12 40 5 ] это результат функции
после: cells = результат ──► [ 12 40 5 ] len 3Форма целиком: срез = append(срез, значение) — имя слева и первым аргументом одно и то же. append отдаёт новый срез, и если его никуда не положить, он пропадёт; почему так — на картинке памяти в день 17.
Одну ошибку компилятор ловит сам — append без присваивания:
stagiaire@lab:~/gocourse/day16/scratch$ go run .
# day16/scratch
./main.go:7:2: append(cells, 12) (value of type []int) is not usedА вот если результат положить не в тот срез — компилятор промолчит.
Попробуйте сейчас: что выведет забытое присваивание.
Цель: предсказать четыре числа и сдать ответ.
1. Не запуская, прочитайте строки тела
main:Код для чтения · разбираем, набирать не нужноvar cells []int for i := 1; i <= 3; i++ { next := append(cells, i*10) fmt.Println(len(next)) } fmt.Println(len(cells))2. Сдайте четыре числа через пробел, в порядке печати:
▶ Выполните · выделенное замените своимcourse answer day16.q2 ЧИСЛО ЧИСЛО ЧИСЛО ЧИСЛО3. Проверьте запуском:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { var cells []int for i := 1; i <= 3; i++ { next := append(cells, i*10) fmt.Println(len(next)) } fmt.Println(len(cells)) }▶ Выполнитеcd ~/gocourse/day16/scratch go run .Готово, когда: пункт
q2вcourse check 16зелёный.
Разбор. На каждом проходе append берёт пустой cells, дописывает одно число и отдаёт срез длины 1 — он попадает в next и забывается. В cells результат не вернулся ни разу. Программа собралась, go vet молчит — ошибка видна только по выводу.
Весь ввод — в срез
Теперь соединим append с чтением из дня 07. Программа целиком:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
var cells []int
for scanner.Scan() {
qty, err := strconv.Atoi(strings.TrimSpace(scanner.Text()))
if err != nil {
fmt.Fprintln(os.Stderr, "Ошибка: не число:", scanner.Text())
os.Exit(1)
}
cells = append(cells, qty)
}
fmt.Println(cells, len(cells))
}Что увидите.
stagiaire@lab:~/gocourse/day16/scratch$ printf '12\n40\n5\n' | go run .
[12 40 5] 3
stagiaire@lab:~/gocourse/day16/scratch$ go run . < /dev/null
[] 0Как это читать. Срез объявлен до цикла: он должен пережить все проходы. Цикл чтения — тот же, что в дне 07: пока есть строка, разобрать число, при ошибке сообщить и выйти. Новая только строка с append: каждое прочитанное число встаёт в конец. После цикла весь ввод лежит в cells, по порядку. Это заготовка для задания stock на пятой странице.
Попробуйте сейчас: ввод в срез.
Цель: программа из раздела выше печатает срез из вашего ввода.
1. Наберите программу из раздела «Весь ввод — в срез» в
scratch/main.go— целиком, вместе с блокомimport:✎ Наберите в файлscratch/main.gopackage main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { scanner := bufio.NewScanner(os.Stdin) var cells []int for scanner.Scan() { qty, err := strconv.Atoi(strings.TrimSpace(scanner.Text())) if err != nil { fmt.Fprintln(os.Stderr, "Ошибка: не число:", scanner.Text()) os.Exit(1) } cells = append(cells, qty) } fmt.Println(cells, len(cells)) }2. Запустите с вводом и без:
▶ Выполнитеcd ~/gocourse/day16/scratch printf '12\n40\n5\n' | go run . go run . < /dev/null3. Перенесите внутрь цикла две строки:
var cells []int— первой строкой тела,fmt.Println(cells, len(cells))— последней. Запустите с тем же вводом:▶ Выполнитеprintf '12\n40\n5\n' | go run .Вывод · это печатает программа, набирать не нужно[12] 1 [40] 1 [5] 1Верните обе строки на место.
Готово, когда: программа печатает
[12 40 5] 3, а опыт из шага 3 вы объяснили себе словами.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
append(cells, 12) (value of type []int) is not used |
результат append никуда не положен |
cells = append(cells, 12) |
invalid append: argument must be a slice; have n (variable of type int) |
первым аргументом стоит число, а не срез | первым — срез, за ним значения |
cannot use "7" (untyped string constant) as int value in argument to append |
в срез целых дописывают строку | тип значения — как у элементов среза |
panic: runtime error: index out of range [0] with length 0 |
в пустой срез пишут по индексу: cells[0] = 12 |
элемента 0 ещё нет — новый элемент добавляет только append |
в срезе всё время одно число: [12] 1, [40] 1, [5] 1 |
срез объявлен внутри цикла и создаётся заново на каждом проходе | объявление — до цикла |
| длина всё время 1, а после цикла срез пустой | результат append ушёл в новую переменную — next := append(…) или cells := append(…) внутри цикла |
cells = append(cells, …) со знаком = |
Второй вариант из последней строки — cells := append(…) — тот же дефект затенения, что в дне 12: := внутри блока создаёт новую переменную с тем же именем.
Попробуйте сейчас: починить fix1.
Цель: программа приёмки считает все ячейки и их сумму.
1. Запустите заготовку:
▶ Выполнитеcd ~/gocourse/day16/fix1 cat example.txt go run . < example.txtЧто сейчас получается:
Вывод · это печатает программа, набирать не нужноПринята ячейка 1 Принята ячейка 1 Принята ячейка 1 Ячеек: 0 Всего: 0 шт.2. Найдите строку, где результат
appendуходит не туда, и почините её — одна-две строки, не больше. Номера ячеек должны расти: 1, 2, 3.3. Проверьте и закоммитьте:
▶ Выполнитеgo run . < example.txt diff ~/.course/orig/day16/fix1/main.go main.go cd ~/gocourse/day16 git add . git commit -m "fix1: срез после append"Готово, когда: программа печатает
Ячеек: 3иВсего: 57 шт.,diffпоказывает одну изменённую строку — пунктfix1.
Дальше: как пройти по срезу и что бывает, если выйти за границу — course next