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

Сравнения и bool

До сих пор программы при каждом запуске делали одно и то же: строка за строкой, сверху вниз. Программе склада этого мало. Хватает ли остатка на заказ? Набрала ли сумма порог скидки? В какую зону везти посылку? Ответ зависит от чисел, и программа должна выбирать, что делать дальше.

Выбор начинается с вопроса, на который ответ — «да» или «нет». Модуль дня — ~/gocourse/day06, черновик для опытов — папка scratch в нём (меняйте как угодно, проверка его не смотрит).


Шесть сравнений

Ситуация. На складе 40 штук, в заказе 25. Отгружать?

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

import "fmt"

func main() {
	stock := 40
	ordered := 25
	fmt.Println(stock >= ordered)
	fmt.Println(stock == ordered)
	fmt.Println(stock < ordered)
}

Что увидите.

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

Как это читать. Сравнение берёт два значения и отвечает true («истина», да) или false («ложь», нет). Третьего не бывает.

Запись Читается При stock 40 и ordered 25
stock == ordered stock равно ordered false
stock != ordered stock не равно ordered true
stock < ordered stock меньше ordered false
stock > ordered stock больше ordered true
stock <= 40 stock меньше или равно 40 true
stock >= ordered stock больше или равно ordered true

Три вещи, на которых спотыкаются в первый день:

Главное про границы: >= включает само число, > — нет. При 25 штуках на складе и 25 в заказе stock >= ordered — true, а stock > ordered — false. На этой разнице держится половина ошибок в скидках и порогах; подробно — на третьей странице.


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

Цель: предсказать на бумаге, что напечатают сравнения, и сверить с запуском.

1. Сотрите заготовку в scratch/main.go и наберите программу про остаток и заказ:

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

import "fmt"

func main() {
	stock := 40
	ordered := 25
	fmt.Println(stock >= ordered)
	fmt.Println(stock == ordered)
	fmt.Println(stock < ordered)
}

Запустите:

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

2. Поставьте ordered := 40 и до запуска запишите на бумаге три строки, которые напечатает программа. Потом запустите go run . и сверьте.

3. Добавьте четвёртую строку fmt.Println(stock > ordered) — что изменилось бы, если бы вместо >= был >?

Готово, когда: первый запуск напечатал true, false, false, как в «Что увидите»; при ordered := 40 вывод совпал с записанным на бумаге, и вы можете сказать, чем >= отличается от > на равных числах (пункт проверки t_gorun).


Ответ сравнения — значение типа bool

Ситуация. Ответ на вопрос «хватает ли товара» нужен в нескольких местах программы. Его можно вычислить один раз и запомнить.

Дальше показываем только строки из тела main — вставляйте их между { и } в scratch/main.go. С одним import "fmt" первая строка тела — шестая. Прежние строки между скобками сотрите. Номера строк в сообщениях — от начала файла.

Код для чтения · разбираем, набирать не нужно
	stock := 40
	ordered := 55
	enough := stock >= ordered
	fmt.Println("Хватает:", enough)
	fmt.Printf("%T\n", enough)
	var paid bool
	fmt.Println("Оплачен:", paid)
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
Хватает: false
bool
Оплачен: false

Как это читать. Тип bool знаком по дню 03: у него два значения, true и false. Теперь видно, откуда они берутся: сравнение вычисляет значение типа bool, как stock - ordered вычисляет int.

Строка Что значит
enough := stock >= ordered обычное объявление: справа выражение, его результат кладётся в новую переменную; тип bool взят из значения
fmt.Printf("%T\n", enough) %T из дня 03 печатает тип — bool
var paid bool без значения — нулевое значение bool, это false

Имена переменных bool читаются как утверждение: enough — «хватает», paid — «оплачен», isHeavy — «тяжёлый». Тогда условие на следующей странице читается как фраза: «если хватает — отгружаем».

true и false пишутся маленькими буквами и без кавычек. "true" в кавычках — строка из четырёх букв, и с bool её не сравнить.


Сравнение строк

Ситуация. Заказ едет в Казань — его нужно отдать казанскому складу.

Код для чтения · разбираем, набирать не нужно
	city := "Казань"
	fmt.Println(city == "Казань")
	fmt.Println(city == "казань")
	fmt.Println(city == "Казань ")
	fmt.Println(city != "Самара")
Вывод · это печатает программа, набирать не нужно
true
false
false
true

Как это читать. Строки равны, только если совпадают полностью: те же буквы, тот же регистр, те же пробелы. «казань» с маленькой буквы и «Казань » с пробелом в конце — для программы другие города.

Когда строку набирал человек, её сначала приводят к одному виду функциями из дня 04. Этому опыту нужен ещё пакет strings, поэтому программа — целиком:

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

import (
	"fmt"
	"strings"
)

func main() {
	city := "  казань "
	fmt.Println(city == "Казань")
	fmt.Println(strings.ToLower(strings.TrimSpace(city)) == "казань")
}
Вывод · это печатает программа, набирать не нужно
false
true

Вложенный вызов читается изнутри наружу: TrimSpace убрал пробелы по краям, ToLower сделал буквы строчными, результат сравнили с образцом, записанным строчными буквами.

После опыта верните import "fmt". Если оставить strings без вызовов, сборка остановится на ошибке "strings" imported and not used, а номера строк во фрагментах дальше разойдутся со страницей.

Знаки < и > у строк тоже работают, но сравнивают не смысл, а коды символов по порядку:

Код для чтения · разбираем, набирать не нужно
	fmt.Println("Болт" < "Винт")
	fmt.Println("болт" < "Винт")
	fmt.Println("10" < "9")
	fmt.Println(10 < 9)
Вывод · это печатает программа, набирать не нужно
true
false
true
false

Строчная «б» оказалась дальше заглавной «В»: у заглавных букв от А до Я коды меньше. "10" < "9" — true: первый символ "1" раньше "9", дальше не смотрят. В курсе строки сравнивают только на == и !=.


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

Цель: предсказать строку вывода и сдать её как ответ day06.q1.

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

Код для чтения · разбираем, набирать не нужно
	a := "Склад"
	b := "склад"
	fmt.Println(a == b, len(b) > 5, 12 >= 12)

Вспомните, что считает len у русских букв (день 04).

2. Сдайте предсказание — до запуска, строку ровно так, как её напечатает программа:

▶ Выполните · выделенное замените своим
course answer day06.q1 ОТВЕТ

Вместо ОТВЕТ — строка вывода целиком.

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

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

import "fmt"

func main() {
	a := "Склад"
	b := "склад"
	fmt.Println(a == b, len(b) > 5, 12 >= 12)
}
▶ Выполните
cd ~/gocourse/day06/scratch
go run .

Готово, когда: ответ сдан до запуска, и запуск его подтвердил; в course check 06 пункт «Что выведет: сравнение строк, len и >=» (q1) отмечен ✔.


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

Сравнивать можно только значения одного типа:

Код для чтения · разбираем, набирать не нужно
	weight := 2.5
	limit := 3
	fmt.Println(weight < limit)
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day06/scratch$ go run .
# day06/scratch
./main.go:8:23: invalid operation: weight < limit (mismatched types float64 and int)

weight — float64, limit — int, и Go не угадывает, как их сравнить. Решение из дня 03: weight < float64(limit). Число, написанное прямо в коде, подстраивается под переменную: weight < 3 соберётся.

Дробные числа сравнивать на равенство опасно, даже когда программа собралась:

Код для чтения · разбираем, набирать не нужно
	a := 0.1
	b := 0.2
	fmt.Println(a+b == 0.3)
Вывод · это печатает программа, набирать не нужно
false

float64 хранит 0,1 и 0,2 чуть неточно, и сумма получается не ровно 0,3. Поэтому деньги в курсе — целые копейки, вес — целые граммы.

Что видите Что это значит Что делать
invalid operation: weight < limit (mismatched types float64 and int) сравниваются float64 и int привести к одному типу: float64(limit)
invalid operation: code == 5 (mismatched types string and untyped int) строку сравнивают с числом: code := "5" сравнивать со строкой "5" или хранить число в int
invalid operation: paid == true (mismatched types string and untyped bool) в переменной строка "true", а не bool объявить paid := true без кавычек
2.5 (untyped float constant) truncated to int целую переменную сравнивают с дробным числом: qty < 2.5 решить, какая граница имелась в виду, и написать целое
syntax error: unexpected = in argument list; possibly missing comma or ) в Println(...) написано =>, =< или одиночное = >=, <= или ==
invalid character U+2265 '≥' in identifier и следом ещё syntax error знак ≥ скопирован из текста набрать >= двумя символами
false там, где ждали true, при сравнении дробных 0.1 + 0.2 в float64 — не ровно 0,3 считать в целых: копейки, граммы

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

Цель: увидеть своими глазами три сообщения компилятора из таблицы и записать их в словарик.

1. Первая ошибка — сравнение float64 с int. Наберите в scratch/main.go:

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

import "fmt"

func main() {
	weight := 2.5
	limit := 3
	fmt.Println(weight < limit)
}
▶ Выполните
cd ~/gocourse/day06/scratch
go run .

2. Две остальные вызовите в той же программе по образцу таблицы: сравните строку с числом (code := "5", а в Println — code == 5), потом наберите => вместо >=. Каждый раз запускайте и прочитайте сообщение целиком:

▶ Выполните
go run .

3. Словарик ~/errors.md ведётся с дня 01: строка «текст ошибки — что значило», через тире. К концу дня в нём должно быть минимум двенадцать разборов — вчерашние вместе с сегодняшними.

Готово, когда: на экране по очереди были три сообщения — с mismatched types float64 and int, с mismatched types string and untyped int и syntax error: unexpected = …; каждое записано строкой в ~/errors.md (к концу дня — пункт проверки errors).


Дальше: if и else — выполнить строки, только когда условие истинно — course next