День 06 · Условия: if, else, switch · страница 5 из 6

И, или, не

Условия из жизни редко бывают из одной части. Доставка бесплатная, если сумма от 3000 руб. и зона — город. Заказ на контроле, если он не оплачен или срочный. Для таких условий есть три логические операции.


&& — «и»

Ситуация. Бесплатная доставка — только для заказов от 3000 руб. в первую зону.

Код для чтения · разбираем, набирать не нужно
	sum := 350000
	zone := 1
	if sum >= 300000 && zone == 1 {
		fmt.Println("Доставка бесплатно")
	} else {
		fmt.Println("Доставка платная")
	}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
Доставка бесплатно

Как это читать. && соединяет два значения bool и даёт true, только если оба true. Хватит одного false — всё условие false: при zone := 2 доставка платная, какой бы ни была сумма.

|| — «или» и ! — «не»

|| (две вертикальные черты: в английской раскладке Shift и \) даёт true, если хотя бы одно значение true. ! перед значением меняет его на противоположное: !true — false.

Код для чтения · разбираем, набирать не нужно
	paid := false
	urgent := true
	if !paid || urgent {
		fmt.Println("Заказ на контроле")
	}
	fmt.Println(!paid, paid || urgent, paid && urgent)
Вывод · это печатает программа, набирать не нужно
Заказ на контроле
true true false

Все случаи для двух значений a и b:

a b a && b — «и» «или» !a — «не»
true true true true false
true false false true false
false true false true true
false false false false true

В столбце «или» — значение a || b.

! пишется вплотную к значению: !paid читается «не оплачен». Работает только с bool — !stock для числа не соберётся.


Попробуйте сейчас: четыре сочетания paid и urgent.

Цель: предсказать !, «или» и && для всех четырёх сочетаний и сверить с запуском.

1. Наберите в scratch/main.go программу с paid и urgent целиком:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	paid := false
	urgent := true
	if !paid || urgent {
		fmt.Println("Заказ на контроле")
	}
	fmt.Println(!paid, paid || urgent, paid && urgent)
}

2. Переберите все четыре сочетания true и false и перед каждым запуском предскажите вторую строку вывода:

▶ Выполните
cd ~/gocourse/day06/scratch
go run .

Готово, когда: на всех четырёх сочетаниях вторая строка вывода совпала с предсказанием и с таблицей выше.


Порядок операций и скобки

Как умножение выполняется раньше сложения, так ! выполняется раньше

&&, а && — раньше ||:

Код для чтения · разбираем, набирать не нужно
	paid := true
	urgent := false
	vip := false
	fmt.Println(paid || urgent && vip)
	fmt.Println((paid || urgent) && vip)
Вывод · это печатает программа, набирать не нужно
true
false

В первой строке сначала urgent && vip — false, потом paid || false — true. Во второй скобки заставили сначала вычислить «или». Правило для

своего кода: если в условии есть и &&, и || — ставьте скобки, даже когда порядок и так верный. Читающему не придётся вспоминать таблицу старшинства.


Короткое вычисление

Ситуация. Товаров 12, коробок пока 0. Нужно предупредить, если на коробку приходится больше трёх товаров.

Код для чтения · разбираем, набирать не нужно
	boxes := 0
	items := 12
	if items/boxes > 3 && boxes != 0 {
		fmt.Println("Коробки переполнены")
	}
	fmt.Println("Готово")
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
panic: runtime error: integer divide by zero

goroutine 1 [running]:
main.main()
	/home/stagiaire/gocourse/day06/scratch/main.go:8 +0x9
exit status 2

Последняя часть строки со стеком — +0x9 — зависит от сборки и версии компилятора: у вас там может быть другое число. Всё остальное, включая goroutine 1 [running]: и exit status 2, будет ровно такое.

Паника из дня 03: деление на переменную, в которой ноль. Проверка boxes != 0 есть, но стоит справа — до неё дело не дошло.

Поменяйте части местами:

Код для чтения · разбираем, набирать не нужно
	boxes := 0
	items := 12
	if boxes != 0 && items/boxes > 3 {
		fmt.Println("Коробки переполнены")
	}
	fmt.Println("Готово")
Вывод · это печатает программа, набирать не нужно
Готово

Как это читать. && и || вычисляют части слева направо и останавливаются, как только ответ ясен:

Здесь boxes != 0 — false, и деление справа не выполняется вовсе. Это называется коротким вычислением. Правило: проверка, которая защищает («не ноль», «есть товар»), стоит слева от того, что без неё сломается.


Попробуйте сейчас: что выведет программа?

Цель: для каждого if решить, вычисляется ли правая часть, и сдать ответ day06.q3.

1. Блок ниже только для чтения — пройдите его в голове:

Код для чтения · разбираем, набирать не нужно
	boxes := 0
	items := 12
	code := 0
	if boxes == 0 || items/boxes > 100 {
		code = code + 1
	}
	if boxes > 0 && items/boxes > 3 {
		code = code + 10
	}
	if items > 10 || items/boxes > 3 {
		code = code + 100
	}
	fmt.Println("Код проверки:", code)

Каждое сработавшее условие добавляет свою цифру: по числу видно, какие сработали. Для каждого if решите, вычисляется ли правая часть. Если считаете, что программа упадёт, так и ответьте.

2. Сдайте предсказание — до запуска:

▶ Выполните · выделенное замените своим
course answer day06.q3 ЧИСЛО

Вместо ЧИСЛО — ваш ответ: число после «Код проверки:».

3. Теперь проверьте себя запуском. Наберите в scratch/main.go ту же программу целиком:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	boxes := 0
	items := 12
	code := 0
	if boxes == 0 || items/boxes > 100 {
		code = code + 1
	}
	if boxes > 0 && items/boxes > 3 {
		code = code + 10
	}
	if items > 10 || items/boxes > 3 {
		code = code + 100
	}
	fmt.Println("Код проверки:", code)
}
▶ Выполните
cd ~/gocourse/day06/scratch
go run .

Готово, когда: ответ сдан до запуска, и запуск его подтвердил;

в course check 06 пункт «Что выведет: короткое вычисление && и ||» (q3) отмечен ✔.


Вложенные if или &&

Условие «сумма от 3000 руб. и первая зона» можно записать вложенным if: внутри блока if sum >= 300000 { ещё один if zone == 1 {. Результат тот же, что у одного if sum >= 300000 && zone == 1 {, но && короче и читается одной фразой — берите его, когда действие одно. Вложенный if оправдан, когда у внутренней проверки своё else, которое имеет смысл только при выполненном внешнем условии: при сумме от 3000 руб. первая зона — доставка бесплатно, остальные — скидка на доставку, а при меньшей сумме — тариф. Через && пришлось бы дважды писать sum >= 300000. Больше двух уровней вложенности читать трудно — тогда цепочку перестраивают.


go vet и подозрительные условия

Ситуация. Хотели «зона не 1 и не 2», а написали «или»:

Код для чтения · разбираем, набирать не нужно
	zone := 2
	if zone != 1 || zone != 2 {
		fmt.Println("Зона не 1 и не 2")
	}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
Зона не 1 и не 2
stagiaire@lab:~/gocourse/day06/scratch$ go vet .
main.go:7:5: suspect or: zone != 1 || zone != 2

Программа собралась и напечатала неправду. Условие истинно при любой зоне: 2 не равно 1, 1 не равно 2, а любое другое число не равно ни тому, ни другому. go vet (день 03) такие условия узнаёт: suspect or — «подозрительное или». Верно — zone != 1 && zone != 2. Смысл порогов

vet не проверяет, но самые частые описки в && и || находит.


Что может пойти не так

Что видите Что это значит Что делать
syntax error: unexpected name and, expected { написано слово and &&; «или» — две вертикальные черты, «не» — !
invalid operation: sum >= 300000 & zone == 1 (mismatched types untyped bool and untyped int) один знак & вместо двух &&
invalid operation: operator ! not defined on stock (variable of type int) ! перед числом сравнение: stock == 0
invalid operation: 1000 <= weight <= 5000 (mismatched types untyped bool and untyped int) двойное сравнение, как в математике две части через &&: weight >= 1000 && weight <= 5000
panic: runtime error: integer divide by zero в строке с && деление стоит левее проверки на ноль проверку на ноль — левой частью
go vet: suspect or:, дальше два != через «или» (две вертикальные черты) — как в примере выше условие истинно всегда && вместо «или»

Попробуйте сейчас: go vet и двойное сравнение.

Цель: go vet . находит «подозрительное или» и замолкает после исправления; двойное сравнение переписано через &&.

1. Наберите в scratch/main.go программу с zone != 1 целиком — тогда и номер строки в сообщении vet совпадёт:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	zone := 2
	if zone != 1 || zone != 2 {
		fmt.Println("Зона не 1 и не 2")
	}
}

Запустите:

▶ Выполните
cd ~/gocourse/day06/scratch
go vet .

2. Исправьте условие и запустите go vet . ещё раз — он ничего не напечатает.

3. Двойное сравнение попробуйте тоже — наберите в scratch/main.go:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	weight := 3700
	if 1000 <= weight <= 5000 {
		fmt.Println("средний")
	}
}
▶ Выполните
go run .

Прочитайте сообщение и перепишите условие через &&.

4. Новые сообщения — в ~/errors.md.

Готово, когда: первый go vet . напечатал suspect or: …, второй — ничего; на двойном сравнении было сообщение invalid operation: 1000 <= weight <= 5000 …, а вариант с && собирается; сообщения записаны в ~/errors.md (пункт проверки t_vet).


Дальше: switch — выбор из многих вариантов — course next