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

if и else

Сравнение отвечает true или false. Теперь этим ответом нужно распорядиться: одни строки выполнить, только если товара хватает, другие — только если не хватает.


if: выполнить, только если

Ситуация. Программа отгрузки. Если остатка хватает — напечатать, сколько штук отгружаем. Если не хватает — не отгружать ничего.

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := 40
	ordered := 25
	if stock >= ordered {
		fmt.Println("Отгружаем:", ordered, "шт.")
	}
	fmt.Println("Проверка заказа закончена")
}

Что увидите.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
Отгружаем: 25 шт.
Проверка заказа закончена

Поменяйте ordered := 25 на ordered := 55:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
Проверка заказа закончена

Как это читать.

Кусок Что значит
if «если»
stock >= ordered условие — любое выражение типа bool
{ в конце строки начало блока: строк, которые выполнятся, только если условие true
} на строке 10 конец блока; дальше программа идёт как обычно

Порядок выполнения. Строки 6 и 7 — как всегда. На строке 8 исполнитель вычисляет условие. Если true, он заходит в блок, выполняет строку 9 и после } переходит к строке 11. Если false, он перескакивает весь блок и сразу выполняет строку 11. Строка 11 выполняется в обоих случаях: она стоит после блока.

Образец · набирать не нужно
            stock >= ordered ?
             /            \
          true            false
           |                |
   Println("Отгружаем:" …)  |
             \             /
   Println("Проверка заказа закончена")

Правила записи — их проверяет компилятор:


Попробуйте сейчас: if на трёх заказах.

Цель: увидеть, когда блок if выполняется, а когда пропускается, и предсказать случай «ровно столько, сколько на складе».

1. Наберите в scratch/main.go программу с if:

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

import "fmt"

func main() {
	stock := 40
	ordered := 25
	if stock >= ordered {
		fmt.Println("Отгружаем:", ordered, "шт.")
	}
	fmt.Println("Проверка заказа закончена")
}

Запустите, потом поменяйте ordered := 25 на ordered := 55 и запустите снова:

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

2. Третий запуск — с ordered := 40, ровно столько, сколько на складе. Предскажите вывод до запуска: >= включает равенство или нет? Потом go run ..

Готово, когда: при 25 и 55 вывод совпал с «Что увидите» выше, а при 40 — с вашим предсказанием.


else: иначе

Ситуация. Если товара не хватает, программа должна сказать, сколько штук не хватает.

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := 40
	ordered := 55
	if stock >= ordered {
		fmt.Println("Отгружаем:", ordered, "шт.")
	} else {
		fmt.Println("Не хватает:", ordered-stock, "шт.")
	}
	fmt.Println("Проверка заказа закончена")
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
Не хватает: 15 шт.
Проверка заказа закончена

Как это читать. else — «иначе». Из двух блоков выполняется ровно один: при true — первый (строка 9), при false — второй (строка 11). Не бывает, чтобы выполнились оба или ни одного.

Во втором блоке ordered-stock не может стать отрицательным: сюда программа попадает, только когда stock >= ordered оказалось false, то есть заказ больше остатка. Каждая ветка «знает», при каком условии в неё пришли, — это пригодится на следующей странице.

} else { пишется одной строкой: закрывающая скобка первого блока, слово else, открывающая скобка второго. else на отдельной строке — ошибка компиляции, она в таблице ниже.


Переменная внутри блока

Ситуация. Статус заказа выбирается в ветках, а печатается после них.

Код для чтения · разбираем, набирать не нужно
	stock := 40
	ordered := 55
	if stock >= ordered {
		status := "отгружаем"
	} else {
		status := "ждём поставку"
	}
	fmt.Println("Статус:", status)
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
# day06/scratch
./main.go:9:3: declared and not used: status
./main.go:11:3: declared and not used: status
./main.go:13:31: undefined: status

Как это читать. Переменная, объявленная внутри { }, существует только до своей }. Здесь объявлены две разные переменные status, каждая в своём блоке, и ни одна там не использована — отсюда два declared and not used. На строке 13 обе уже исчезли — undefined. Три сообщения, а причина одна: объявление стоит не на том уровне.

Часть программы, где имя видно, называется областью видимости. Подробнее о ней — в блоке 3, когда появятся свои функции.

Правильно — объявить переменную до if, а в ветках только присваивать, знаком =:

Код для чтения · разбираем, набирать не нужно
	stock := 40
	ordered := 55
	var status string
	if stock >= ordered {
		status = "отгружаем"
	} else {
		status = "ждём поставку"
	}
	fmt.Println("Статус:", status)
Вывод · это печатает программа, набирать не нужно
Статус: ждём поставку

Опаснее всего := в ветке, когда снаружи уже есть переменная с тем же именем. Программа соберётся, но := создаст внутри блока новую переменную, а наружная останется прежней:

Код для чтения · разбираем, набирать не нужно
	price := 1000
	if price > 500 {
		price := price - 100
		fmt.Println(price)
	}
	fmt.Println(price)
Вывод · это печатает программа, набирать не нужно
900
1000

Попробуйте сейчас: переменная внутри блока.

Цель: прочитать три сообщения об объявлении в ветке и починить программу переносом объявления.

1. Наберите в scratch/main.go программу со status := в ветках — целиком, чтобы номера строк в сообщениях совпали с вашими:

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

import "fmt"

func main() {
	stock := 40
	ordered := 55
	if stock >= ordered {
		status := "отгружаем"
	} else {
		status := "ждём поставку"
	}
	fmt.Println("Статус:", status)
}

Запустите и прочитайте все три сообщения:

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

2. Почините её переносом объявления до if и запустите go run . снова.

3. Поставьте ordered := 30 и предскажите статус до запуска.

Готово, когда: первый запуск дал два declared and not used: status и undefined: status; после починки программа напечатала Статус: ждём поставку; при ordered := 30 статус совпал с предсказанием.


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

Число вместо условия. В некоторых языках число в условии значит «не ноль». В Go условие — только bool:

Код для чтения · разбираем, набирать не нужно
	stock := 40
	if stock {
		fmt.Println("Товар есть")
	}
Вывод · это печатает программа, набирать не нужно
# day06/scratch
./main.go:7:5: non-boolean condition in if statement

= вместо ==. В условии нужно сравнение, а написано присваивание:

Код для чтения · разбираем, набирать не нужно
	stock := 0
	if stock = 0 {
		fmt.Println("Товара нет")
	}
Вывод · это печатает программа, набирать не нужно
# day06/scratch
./main.go:7:11: syntax error: cannot use assignment stock = 0 as value

else на новой строке:

Код для чтения · разбираем, набирать не нужно
	stock := 40
	ordered := 55
	if stock >= ordered {
		fmt.Println("Отгружаем")
	}
	else {
		fmt.Println("Не хватает")
	}
Вывод · это печатает программа, набирать не нужно
# day06/scratch
./main.go:11:2: syntax error: unexpected keyword else, expected }

Компилятор поставил точку с запятой после } на строке 10 и счёл if законченным. Строка, начинающаяся с else, ему непонятна.

Что видите Что это значит Что делать
non-boolean condition in if statement в условии не bool, а число или строка написать сравнение: stock > 0
syntax error: cannot use assignment stock = 0 as value в условии = вместо == ==
syntax error: missing condition in if statement после if условия нет вовсе: if { написать условие: if stock > 0 {
syntax error: unexpected keyword else, expected } else перенесён на новую строку } else { одной строкой
syntax error: unexpected newline, expected { after if clause { перенесена на следующую строку или забыта { в конце строки с if
syntax error: else must be followed by if or statement block и следом non-declaration statement outside function body после else написано условие: } else days > 3 { у else условия не бывает; нужно условие — else if, следующая страница
declared and not used: left на строке внутри блока переменную объявили в ветке и не использовали в ней использовать в блоке или объявить до if
undefined: status после } переменная объявлена внутри блока и за ним не существует объявить до if, в ветках — =
собралось, но после if у переменной старое значение в ветке := создал новую переменную в ветке =

Попробуйте сейчас: починка fix2.

Цель: fix2 собирается и при нулевом и ненулевом остатке печатает то, что обещано в комментарии в начале файла.

1. Программа fix2 не собирается. Что она должна делать — в комментарии в начале файла. Запустите:

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

2. Найдите строку из сообщения, почините её и запустите go run . снова. Хватает одной строки; проверка принимает не больше двух — тот же лимит записан в course lab 06.

3. Теперь посмотрите вторую ветку: поставьте stock := 5, запустите и верните обратно stock := 0. Это опыт, а не часть починки: строка stock := 0 должна остаться такой, какой была, — проверка сама подставляет в неё разные остатки. Возвращённая строка в размер правки не попадёт, забытая stock := 5 — попадёт.

4. Посмотрите, что изменили, сравнив с оригиналом:

▶ Выполните
diff ~/.course/orig/day06/fix2/main.go main.go

Строки с < — как было, с > — как стало. Новые сообщения запишите в ~/errors.md, через тире.

Готово, когда: при stock := 0 и stock := 5 программа собирается и печатает обещанное в комментарии, в файле снова stock := 0, а diff показывает только вашу правку (пункты проверки fix2 и t_diff).


Дальше: цепочка else if и почему порядок проверок решает всё — course next