Программа целиком, коммиты и словарик
Осталось собрать сегодняшнее вместе и закрыть день так, как его будут закрывать все оставшиеся дни блока: проверить, отформатировать, закоммитить.
Обе темы в одной программе
Ситуация. Накладная: в каждой строке количество. Нужно на каждую строку сказать, сколько выйдет полных коробок и сколько останется россыпью, а на плохой строке — остановиться.
package main
import (
"bufio"
"errors"
"fmt"
"os"
"strconv"
"strings"
)
func parseQty(s string) (int, error) {
text := strings.TrimSpace(s)
if text == "" {
return 0, errors.New("пустое количество")
}
qty, err := strconv.Atoi(text)
if err != nil {
return 0, fmt.Errorf("не число: %v", err)
}
return qty, nil
}
func pack(qty int, size int) (int, int) {
return qty / size, qty % size
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
size := 6
line := 0
for scanner.Scan() {
line++
qty, err := parseQty(scanner.Text())
if err != nil {
fmt.Fprintf(os.Stderr, "Ошибка в строке %d: %v\n", line, err)
os.Exit(1)
}
boxes, rest := pack(qty, size)
fmt.Printf("строка %d: %d шт. - коробок %d, россыпью %d\n", line, qty, boxes, rest)
}
}Что увидите.
stagiaire@lab:~/gocourse/day12/scratch$ printf '17\n5\n' | go run .
строка 1: 17 шт. - коробок 2, россыпью 5
строка 2: 5 шт. - коробок 0, россыпью 5
stagiaire@lab:~/gocourse/day12/scratch$ printf '17\nx\n' | go run .
строка 1: 17 шт. - коробок 2, россыпью 5
Ошибка в строке 2: не число: strconv.Atoi: parsing "x": invalid syntax
exit status 1Как это читать. В main три строки настоящей работы: взять количество, разложить по коробкам, напечатать. Всё остальное — цикл, номер строки и обработка ошибки. Две функции, у одной два результата, у другой — результат и ошибка; ни одна из них ничего не печатает и не завершает программу.
Ещё раз о порядке в main: сначала вызов, сразу под ним проверка err, и только потом qty идёт в дело. Это тот порядок, в котором вы будете читать чужой Go-код: «вызов — проверка — использование», и если проверки между вызовом и использованием нет, это первое место, куда стоит смотреть при разборе дефекта.
Ритуал перед коммитом
Три команды в папке задания, всегда в одном порядке:
| Команда | Что проверяет |
|---|---|
gofmt -w . |
отступы и пробелы: правит сама |
go vet . |
подозрительные места, которые компилятор пропускает |
go run . < example.txt |
что программа делает то, что нужно |
gofmt и go vet вы знаете с блока 1. Новое — что теперь после них идёт коммит, и в историю попадает уже проверенная работа.
Коммит после каждого задания
Репозиторий и первые коммиты были в дне 11. Сегодня репозиторий вы завели на первой странице, пока ничего не тронуто, — вот так это выглядело:
stagiaire@lab:~/gocourse/day12$ git status --short
?? go.mod
?? split/
stagiaire@lab:~/gocourse/day12$ git add .
stagiaire@lab:~/gocourse/day12$ git commit -m "день 12: заготовки"
[main (root-commit) b07cfc6] день 12: заготовки
2 files changed, 6 insertions(+)
create mode 100644 go.mod
create mode 100644 split/main.goБуквы и цифры после [main — номер коммита, у вас он будет другой: он считается из содержимого и времени. Строки ниже — сколько файлов изменилось и сколько строк добавилось.
После второго коммита история читается одной командой:
stagiaire@lab:~/gocourse/day12$ git log --oneline
944fb03 split: разбор строк заказа
b07cfc6 день 12: заготовки
stagiaire@lab:~/gocourse/day12$ git status --short
stagiaire@lab:~/gocourse/day12$Пустой ответ git status --short означает, что всё сохранённое в файлах уже записано в историю. Это удобная проверка перед тем, как закрыть день.
Почему коммит стоит последним шагом задания: каждое задание дня заканчивалось своими двумя командами — fix2, split, intake, fix1, trace, — и в истории теперь видно, в каком порядке всё происходило. Один коммит в конце дня этого не показывает: в нём вся работа одной записью. Сообщение пишут о том, что сделано, а не о том, какие файлы тронуты: «split: разбор строк заказа» полезнее, чем «правки».
Собранные программы в историю не кладут: go build -o prog . создаёт файл, который получается из кода за секунду. Для таких файлов есть .gitignore — он был в дне 11:
stagiaire@lab:~/gocourse/day12$ go build -o prog ./split
stagiaire@lab:~/gocourse/day12$ printf 'prog\n' > .gitignore
stagiaire@lab:~/gocourse/day12$ git status --short
?? .gitignoreФайл prog в списке не появился — git про него больше не спрашивает, а сам .gitignore коммитят: он часть работы. Если вы повторили этот опыт, закоммитьте и его — иначе git status --short так и будет показывать ?? .gitignore:
stagiaire@lab:~/gocourse/day12$ git add .gitignore
stagiaire@lab:~/gocourse/day12$ git commit -m "не коммитим собранные файлы"
[main dc6cd7a] не коммитим собранные файлы
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
stagiaire@lab:~/gocourse/day12$ git status --short
stagiaire@lab:~/gocourse/day12$Попробуйте сейчас: история дня.
Цель: вся работа дня записана, история видна одной командой.
1. Посмотрите, что записано и не осталось ли незакоммиченного:
▶ Выполнитеcd ~/gocourse/day12 git log --oneline git status --short2. Если
git status --shortчто-то печатает — забытое задание или?? .gitignoreпосле опыта выше, — запишите и это:▶ Выполнитеgit add . git commit -m "что сделали" git log --onelineЕсли
git commitотвечает, что не знает, кто вы, выполните две команды настройки из дня 11 —git config --global user.nameиuser.email— и повторите коммит.Готово, когда:
git log --onelineпоказывает не меньше трёх строк — по коммиту на задание, — аgit status --shortпосле последнего коммита ничего не печатает; пунктыgitиt_gitlogвcourse check 12зелёные.
Что может пойти не так с коммитами
| Что видите | Что это значит | Что делать |
|---|---|---|
fatal: not a git repository (or any of the parent directories): .git |
команда выполняется не в папке с репозиторием | cd ~/gocourse/day12, при необходимости git init |
Author identity unknown и просьба выполнить git config |
подпись автора не настроена | две команды git config --global из дня 11 |
nothing to commit, working tree clean |
всё уже закоммичено | это не ошибка: коммит делают после изменений |
в git status --short строка ?? prog |
в папке лежит собранный файл | добавить его имя в .gitignore |
| коммитов меньше трёх, хотя вы коммитили | часть коммитов сделана в другой папке | git log --oneline в ~/gocourse/day12 покажет, что попало сюда |
git add . добавляет лишние файлы |
в папке остался мусор от опытов | удалить ненужное или перечислить в .gitignore |
Репозиторий сегодня местный: он лежит в той же папке, что и работа, и никуда ничего не отправляет. Общий репозиторий, куда складывают работу всей командой, будет в блоке 7 — механика та же, добавится только обмен.
Словарик ошибок
К концу дня в ~/errors.md — минимум двадцать четыре разбора: за прошлые дни и за сегодня. Строка засчитывается, если в ней текст ошибки, тире с пробелами вокруг и что это значило. Сегодня встречались:
not enough return valuesсо строкамиhave/want— вreturnзначений меньше, чем в заголовке функции;too many return values— наоборот, больше;assignment mismatch: 1 variable but pack returns 2 values— слева одна переменная, а результата два;assignment mismatch: 2 variables but boxes returns 1 value— функция отдаёт одно значение;multiple-value pack(17, 5) … in single-value context— вызов с двумя результатами рядом с другими аргументами;cannot use _ as value or type— из_попытались прочитать;undefined: rest,undefined: bonus,undefined: qty— переменной в этом месте нет: она из другого блока или это параметр функции;declared and not used: qtyна строке с:=— почти всегда затенение;cannot use errors.New(…) as int value in return statement— результаты вreturnпереставлены местами;invalid operation: err != 0 (mismatched types error and untyped int)— ошибку сравнили с числом;fmt.Errorf format %v reads arg #2, but call has 1 argотgo vet— в шаблоне глаголов больше, чем значений;missing return— из функции есть путь безreturn.
Попробуйте сейчас: словарик и проверка дня.
Цель: в
~/errors.mdдвадцать четыре разбора,course check 12показывает, что осталось.1. Допишите сегодняшние ошибки в
~/errors.md— каждая строка «текст ошибки — что значило», через тире.2. Запустите проверку:
▶ Выполнитеcourse check 123. Красные пункты доделайте по страницам, указанным рядом. Между запусками проверки — пауза 15 секунд: это время лучше потратить на запуск самой программы.
Готово, когда: пункт
errorsзелёный и зачтено не меньше 14 пунктов из 17.
Итог дня
Что уносите:
- У функции может быть два результата. В заголовке —
(int, int), вreturn— два значения через запятую, при вызове — две переменные слева в том же порядке. Ненужный результат — в_. - Имя видно от объявления до закрывающей
}своего блока. Функция не видит переменныхmainи других функций: данные передают аргументами и возвращают результатом. - Ошибка — последний результат, типа
error. Ошибка неnil— остальные результаты не берут;nil— результат настоящий. Ошибка с постоянным текстом —errors.New, с добавкой «где случилось» —fmt.Errorf. - Кто вызвал, тот и решает. Функция возвращает ошибку, а не печатает её и не завершает программу;
if err != nilстоит сразу под вызовом. :=внутри блока создаёт новую переменную с тем же именем — затенение.go vetего не замечает; чтобы поменять внешнюю, нужен=.- Трассировка: одноимённые переменные — разные колонки (правило 21), строка с вызовом без расписанных шагов функции — один шаг (правило 22).
- Перед коммитом —
gofmt -w .,go vet .и прогон; коммит — после каждого задания.
Завтра, в день 13, речь пойдёт не об одной функции, а о целой задаче: какие куски вынести, как их назвать и как разложить готовую программу, ничего не сломав.
Материал дня закончен. Задания — course lab 12, дополнительное чтение — course extra 12