Памятка блока 2
Справочник по дням 06–09. Читать подряд не нужно — обязателен только блок «Попробуйте сейчас». Остальное открывайте, когда понадобится. Все сообщения — настоящие, Go 1.27.1.
Попробуйте сейчас: словарик до двадцати разборов.
Цель: в
~/errors.mdне меньше двадцати разборов.1. Недостающие сообщения получите сами: скопируйте
selftestв черновик и ломайте способами из таблицы «Ошибки компилятора», по одному за раз. После каждой поломки — сборка:▶ Выполнитеcp ~/gocourse/day10/selftest/main.go ~/gocourse/day10/scratch/main.go cd ~/gocourse/day10/scratch go build .2. Строка словарика — текст ошибки, тире, что это значило в вашем коде. Двоеточие вместо тире не засчитывается.
Готово, когда: пункт
errorsвcourse check 10зелёный.
Команды
| Команда | Что делает | День |
|---|---|---|
./prog < input.txt |
ввод из файла | 07 |
./prog > out.txt |
обычный вывод — в файл, поток ошибок — на экран | 07 |
./prog 2> err.txt |
поток ошибок — в файл, обычный вывод — на экран | 07 |
go run . < input.txt 2>/dev/null |
поток ошибок пропадает, на экране только результат | 09 |
echo $? |
код возврата последней команды | 07 |
go build -o prog . |
собрать под именем prog |
07 |
| Ctrl+D, Ctrl+C | конец ввода с клавиатуры; остановить программу | 07, 08 |
diff got.txt want.txt |
различия файлов; тишина — совпали | 02, 10 |
for f in cases/*.txt; do …; done |
команды для каждого файла | 10 |
dlv debug -r input.txt |
отладчик с вводом из файла | 09 |
Конвейер: printf '3\n5\n' | ./prog — вывод printf становится вводом программы.
Цифра 2 в 2> — номер потока: у обычного вывода номер 1, и > — это сокращение от 1> (день 07, страница 5). /dev/null — особый файл: всё, что в него записано, пропадает (день 09, страница 2).
| Команда dlv | Что делает |
|---|---|
break main.go:15 |
остановиться перед строкой 15 |
condition 1 boxes == 3 |
точка 1 срабатывает, только когда условие истинно |
continue, next |
до следующей остановки; одна строка |
step, stepout |
как next, но зайти внутрь вызываемой функции; досчитать её и вернуться туда, откуда её вызвали (день 09, страница 4) |
print total, locals |
значение; все переменные функции |
restart, exit |
начать заново; выйти |
Условия — день 06
if weight > maxWeight {
fmt.Println("Доставка невозможна")
} else if sum >= freeFrom {
fmt.Println("Доставка: бесплатно")
} else {
fmt.Println("Доставка платная")
}
switch zone {
case 1:
fmt.Println("город")
case 2, 3:
fmt.Println("область")
default:
fmt.Println("нет доставки")
}| В условии задачи | В коде |
|---|---|
| «больше N» | x > N |
| «N или больше», «от N» | x >= N |
| «до N включительно» | x <= N |
| «меньше N» | x < N |
Срабатывает первая истинная ветка else if: узкое условие — раньше широкого. Правая часть && не вычисляется при ложной левой. Переменная из { } снаружи не видна.
Ввод и ошибки — день 07
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
qty, err := strconv.Atoi(strings.TrimSpace(scanner.Text()))
if err != nil {
fmt.Fprintln(os.Stderr, "ошибка: не число")
os.Exit(1)
}- Ввод кончился —
Scan()возвращаетfalse,Text()— пустую строку. Atoiне принимает пробелы по краям, пустую строку и дробь;"+5"— 5.- Сообщение об ошибке — в
os.Stderr, затемos.Exit(1).
Циклы — день 08
for i := 1; i <= n; i++ { }
for stock > 0 { }
for { if done { break } }
for scanner.Scan() { }| Заголовок | Сколько раз тело |
|---|---|
i := 0; i < n; i++ |
n |
i := 1; i <= n; i++ |
n |
i := 1; i < n; i++ |
n − 1 |
i := n; i > 0; i-- |
n |
Накопитель объявляют до цикла. Минимум и максимум начинают с первого значения, а не с нуля. continue — к следующему проходу, break — выход.
Трассировка: правила 1–15
Правила копились с дня 02 и нумеруются сквозь весь курс. Здесь они собраны целиком — открывать три прошлые страницы не нужно. Это тот же список, по которому сверяется таблица в kt6 контрольной точки; новых правил контрольная не вводит.
Строки и значения (день 02, страница 4).
- Одна строка таблицы — одна выполненная строка кода внутри
main, включая строки с печатью. - Значения записываются после выполнения шага.
- Переменной ещё нет — прочерк
-. Это не ноль. - То, что шаг не трогал, переписывается из строки выше: пустых клеток не бывает.
- Правая часть считается по предыдущей строке таблицы.
- Номера строк — по исходному файлу программы, без ваших вставок с отладочной печатью.
Ветвления (день 06, страница 4).
- Строка с
ifили} else if— шаг; значения в ней те же, что строкой выше: условие только читает. - Строки
} else {и одинокая}— не шаги. - Строки невыполненных веток в таблицу не попадают: номера идут с пропусками.
- После первой сработавшей ветки остальная цепочка — не шаги.
Циклы (день 08, страница 4).
- Строка
for— шаг каждый раз, когда программа к ней приходит. В первый раз выполняются начальное значение и проверка, в следующие — третья часть (i++) и проверка; в таблицу идут значения после всего этого. - Последний шаг со строкой
for— тот, где условие ложно; он есть всегда, и переменная цикла на нём уже «за границей». - После цикла переменной цикла нет — прочерк.
- Строки
breakиcontinue— шаги. Значения в них переписываются из строки выше. Послеcontinueследующий шаг — строкаfor, послеbreak— первая строка после цикла. - Шаги нумеруются подряд, номера строк повторяются. Самопроверка: строка
forвстречается на один раз больше, чем выполнилось тело.
В блоке 3 к этим правилам добавятся 16–24 — про вызовы функций; там их собирает памятка блока 3.
Ошибки компилятора
| Сообщение | Причина | День |
|---|---|---|
syntax error: cannot use assignment count = 0 as value |
= вместо == в условии |
06 |
syntax error: unexpected keyword else, expected } |
else на новой строке после } |
06 |
syntax error: unexpected newline, expected { after if clause |
{ перенесена на следующую строку |
06 |
non-boolean condition in if statement |
в условии не сравнение | 06 |
invalid operation: weight > limit (mismatched types int and float64) |
сравнение int с float64 |
06 |
declared and not used: discount и undefined: discount |
объявлена внутри if, нужна после |
06 |
duplicate case 1 (constant of type int) in expression switch |
два одинаковых case; ниже строка previous case |
06 |
assignment mismatch: 1 variable but strconv.Atoi returns 2 values |
у Atoi два результата |
07 |
declared and not used: err |
err не проверен |
07 |
undefined: err |
было n, _ :=, а ниже if err != nil |
07 |
cannot use scanner.Text() (value of type string) as int value in variable declaration |
строка кладётся в int без Atoi |
07 |
undefined: bufio, undefined: os |
пакета нет в import |
07 |
undefined: i |
переменная цикла после цикла | 08 |
syntax error: unexpected :=, expected ) |
for (i := 0; …) со скобками |
08 |
non-boolean condition in for statement |
for scanner.Scan { без скобок вызова |
08 |
break is not in a loop, switch, or select |
break вне цикла |
08 |
continue is not in a loop |
continue вне цикла |
08 |
go vet: main.go:8:2: unreachable code — строки после return; main.go:9:2: fmt.Println does not take io.Writer but has first arg os.Stderr — Println вместо Fprintln. Строки после os.Exit go vet не замечает.
При запуске: panic: runtime error: integer divide by zero и код 2 — делитель оказался нулём; exit status 1 — строка go run о коде 1. Последнюю в словарик нести не нужно: это не сообщение компилятора, а строка самого go run, и проверка её не засчитывает.
Собирается, но считает не то
| Признак | Дефект | День |
|---|---|---|
| ровно на пороге ответ как ниже порога | в сравнении перепутано строгое с нестрогим | 06 |
| почти всё попадает в одну ветку | широкое условие else if стоит раньше узкого |
06 |
| на один проход больше или меньше | граница цикла, счёт с 0 или с 1 | 08 |
| итог равен последнему значению или нулю | накопитель объявлен внутри цикла | 08 |
| минимум 0 при положительных числах | начальное значение минимума | 08 |
| пустые строки посчитаны как данные | счётчик увеличен раньше проверки, которая их пропускает | 08 |
panic: … divide by zero на пустом вводе |
деление до проверки на пустоту | 08 |
| на числе с пробелами — ошибка ввода | нет strings.TrimSpace |
07 |
| после сообщения об ошибке есть результат | нет os.Exit(1) |
07 |
при > out.txt сообщение попало в файл |
Println вместо Fprintln(os.Stderr, …) |
07 |
| доля или процент вышли меньше, чем должны, или нулём | у целых порядок * и / меняет результат: посчитайте оба порядка на числе, которое не делится нацело |
09 |
| найдено последнее подходящее вместо первого | нет break |
09 |
Отладка — день 09
Воспроизвести (ввод, на котором неверно) → сузить (где значение впервые стало не таким) → гипотеза → проверка → починка → прогон всего набора. Отладочная печать — в поток ошибок и с подписью, перед сдачей её убирают:
fmt.Fprintln(os.Stderr, "отладка:", box, load)Глагол %q печатает строку в кавычках: пробелы по краям и пустая строка становятся видны — "14 ", "" (день 09, страница 2). \n в конце нужен: Printf сам перевод строки не ставит.
fmt.Fprintf(os.Stderr, "отладка: строка %d %q\n", i, line)Дальше: правила контрольной точки 2 — course next