Цепочка else if и порядок проверок
if и else выбирают из двух вариантов. Вариантов часто больше: статус доставки, размер скидки, тип упаковки. Для этого проверки выстраивают в цепочку. Здесь же — главная ловушка дня: цепочка, в которой каждое условие верное, а результат неверный.
else if: несколько вариантов
Ситуация. Статус доставки по числу дней в пути: 0 — отгружен сегодня; от 1 до 3 — в пути; больше трёх — задерживается.
package main
import "fmt"
func main() {
days := 2
if days == 0 {
fmt.Println("Отгружен сегодня")
} else if days <= 3 {
fmt.Println("В пути, дней:", days)
} else {
fmt.Println("Задерживается, дней:", days)
}
}Что увидите.
stagiaire@lab:~/gocourse/day06/scratch$ go run .
В пути, дней: 2Как это читать. Цепочка проверяется сверху вниз:
- Строка 7:
days == 0?2 == 0—false. Блок пропущен, дальше. - Строка 9:
days <= 3?true. Выполняется строка 10. - Всё, что ниже, пропускается:
elseна строке 11 не рассматривается, программа продолжает после последней}.
Правило цепочки: выполняется первая ветка, чьё условие истинно. Условия ниже неё не проверяются вообще. else в конце значит «ни одно условие не подошло». Его можно не писать; тогда при всех false не выполнится ни одна ветка.
Почему на строке 9 не нужно days >= 1 && days <= 3? Потому что до строки 9 программа доходит, только если days == 0 уже оказалось false. Каждое следующее условие цепочки проверяется при условии, что все предыдущие были ложными. Поэтому условия в цепочке короткие — и поэтому их порядок важен.
} else if days <= 3 { — одной строкой, как } else {.
Попробуйте сейчас: ветки цепочки по дням.
Цель: для каждого значения
daysназвать ветку до запуска и сверить с тем, что напечатала программа.1. Наберите в
scratch/main.goпрограмму со статусом доставки:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { days := 2 if days == 0 { fmt.Println("Отгружен сегодня") } else if days <= 3 { fmt.Println("В пути, дней:", days) } else { fmt.Println("Задерживается, дней:", days) } }Запускайте с
daysравным 0, 1, 3, 4. Перед каждым запуском назовите ветку:▶ Выполнитеcd ~/gocourse/day06/scratch go run .2. Потом
days := -1: какую ветку выберет программа и разумно ли это для склада?Готово, когда: на 0, 1, 3 и 4 напечатанная ветка совпала с названной, а для
-1у вас есть ответ, какую ветку выбрала программа и годится ли это складу.
Порядок проверок
Ситуация. Скидка по сумме заказа: от 10 000 руб. — 10 %, от 5000 руб. — 5 %. Суммы в копейках.
sum := 1250000
pct := 0
if sum >= 500000 {
pct = 5
} else if sum >= 1000000 {
pct = 10
}
fmt.Println("Скидка, %:", pct)stagiaire@lab:~/gocourse/day06/scratch$ go run .
Скидка, %: 5Заказ на 12 500 руб. получил 5 % вместо 10. Компилятор молчит, go vet тоже: по правилам языка программа верная, по смыслу — нет.
Как это читать. Строка 8: 1250000 >= 500000 — true, pct = 5, остальная цепочка пропущена. Ветка pct = 10 не выполнится никогда: любая сумма от 10 000 руб. больше 5000 руб. и раньше попадает в первую ветку. Каждое условие по отдельности записано верно, ошибка — в порядке.
Порядок исправлен:
sum := 1250000
pct := 0
if sum >= 1000000 {
pct = 10
} else if sum >= 500000 {
pct = 5
}
fmt.Println("Скидка, %:", pct)Скидка, %: 10Правило порядка. Пороги с >= и > идут от большего к меньшему. Пороги с < и <= — от меньшего к большему. Общее правило: сначала самое узкое условие, потом шире.
Как проверить готовую цепочку: для каждой ветки придумайте число, которое должно в неё попасть, и пройдите цепочку сверху с этим числом. Если число «застряло» в ветке выше своей — порядок неверный.
Отдельные if вместо цепочки — другая ошибка:
sum := 1250000
if sum >= 1000000 {
fmt.Println("Скидка 10%")
}
if sum >= 500000 {
fmt.Println("Скидка 5%")
}Скидка 10%
Скидка 5%Два отдельных if не связаны: каждый проверяется всегда. Когда из нескольких вариантов нужен ровно один, пишут цепочку else if. Отдельные if — для проверок, которые друг друга не исключают: заказ бывает одновременно тяжёлым и срочным.
Попробуйте сейчас: что выведет программа?
Цель: пройти цепочку с границей в голове и сдать ответ
day06.q2.1. Блок ниже только для чтения — пройдите его в голове:
Код для чтения · разбираем, набирать не нужноweight := 1000 if weight > 1000 { fmt.Println("тяжёлый") } else if weight < 0 { fmt.Println("ошибка веса") } else if weight >= 1000 { fmt.Println("средний") } else { fmt.Println("лёгкий") }Вес — ровно 1000, то есть ровно на пороге. Пройдите условия по очереди и помните, чем
>отличается от>=.2. Сдайте предсказание — до запуска:
▶ Выполните · выделенное замените своимcourse answer day06.q2 ОТВЕТВместо
ОТВЕТ— одно слово, как его напечатает программа.3. Теперь проверьте себя запуском. Наберите в
scratch/main.goту же программу целиком:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { weight := 1000 if weight > 1000 { fmt.Println("тяжёлый") } else if weight < 0 { fmt.Println("ошибка веса") } else if weight >= 1000 { fmt.Println("средний") } else { fmt.Println("лёгкий") } }▶ Выполнитеcd ~/gocourse/day06/scratch go run .Готово, когда: ответ сдан до запуска, и запуск его подтвердил; в
course check 06пункт «Что выведет: цепочка else if с границей» (q2) отмечен ✔.
Границы
Ситуация. В условии написано «от 1000 г до 19 999 г — коробка». Какой знак ставить?
| В условии | В коде | Само число входит? |
|---|---|---|
| от 1000 и больше, не меньше 1000 | weight >= 1000 |
да |
| больше 1000, свыше 1000 | weight > 1000 |
нет |
| до 1000 включительно, не больше 1000 | weight <= 1000 |
да |
| меньше 1000 | weight < 1000 |
нет |
«До 1000» без уточнения понимают по-разному. В заданиях курса всегда сказано, входит ли граница.
Ошибка > вместо >= видна только на самой границе. На 999 и 1001 обе записи дают одно и то же, расходятся они ровно на 1000. Поэтому примеры по методу дня 05 для каждой границы — само число и соседнее: для «от 1000» — 999 и 1000. Проверка заданий подставляет в программу именно такие числа.
Для целых чисел weight >= 1000 и weight > 999 означают одно и то же. Пишите так, как сказано в условии: число из условия в коде проще сверить глазами.
Попробуйте сейчас: починка порядка в fix1.
Цель:
fix1выбирает упаковку по комментарию на всех границах.1. Программа
fix1собирается, но выбирает не ту упаковку. Запустите:▶ Выполнитеcd ~/gocourse/day06/fix1 go run .2. Пройдите цепочку сверху вниз с весом из комментария: какое условие сработало первым?
3. Почините порядок — поменяйте ветки местами, это до четырёх строк.
4. Проверьте все границы: поставьте в строку
weight :=по очереди 999, 1000, 19999, 20000, каждый разgo run ., и сверьте с комментарием. Потом верните исходный вес.Готово, когда: на весах 999, 1000, 19999, 20000 и на исходном весе программа печатает упаковку из комментария, в файле снова исходный вес (пункт проверки
fix1).
Что может пойти не так
Ошибки цепочек почти всегда логические: программа собирается и работает, но выбирает не ту ветку. Пример — особый случай, который не проверили:
stock := -3
status := ""
if stock == 0 {
status = "нет в наличии"
} else if stock < 10 {
status = "мало"
} else {
status = "достаточно"
}
fmt.Println(stock, status)-3 малоОтрицательный остаток — ошибка учёта, а программа назвала его «мало»: отдельной проверки для него нет, и первым подошло stock < 10. Особые случаи — отрицательное число, ноль — проверяют первыми.
| Что видите | Что это значит | Что делать |
|---|---|---|
| большой заказ получил маленькую скидку, тяжёлый груз — тариф среднего | широкое условие стоит выше узкого | узкие условия выше; пройти цепочку с числом для каждой ветки |
| напечатались две ветки вместо одной | отдельные if вместо else if |
} else if … { |
| на числе ровно на границе — не та ветка | > вместо >= или < вместо <= |
сверить знак с условием, проверить число и соседнее |
| особое значение (ноль, минус) попало в обычную ветку | для него нет своей проверки | проверка особого случая — первой |
| ни одна ветка не выполнилась, у переменной начальное значение | нет else, и ни одно условие не подошло |
добавить else для всего остального |
syntax error: unexpected keyword else, expected } |
else if перенесён на новую строку после } |
} else if … { одной строкой |
syntax error: unexpected newline, expected { after if clause на строке с else if |
{ перенесена на следующую строку |
{ в конце строки |
Попробуйте сейчас: задание stock.
Цель:
stock/main.goназывает статус остатка поTASK.txtдля любых чисел, числа у каждого свои.1. Прочитайте условие:
▶ Выполнитеcd ~/gocourse/day06/stock cat TASK.txt2. По методу дня 05: сначала выпишите примеры — по одному на каждый статус и по два на каждую границу. Решите, какой статус проверять первым.
3. Напишите
main.go, запуститеgo run .и прогоните все свои примеры, меняя числа в первых строках.4. Верните свои числа и проверьте формат:
▶ Выполнитеgofmt -l .Пустой вывод — код отформатирован.
Готово, когда: на каждом примере с бумаги программа печатает статус по условию, в первых строках снова ваши числа,
gofmt -l .ничего не печатает (пункты проверкиstock,t_gofmt,code_fmt).
Дальше: как трассировать программу с ветвлениями — course next