Сравнения и 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 |
Три вещи, на которых спотыкаются в первый день:
- Равенство — два знака
==. Один знак=— присваивание из дня 02: «положить значение в ячейку».==— вопрос «одинаковы ли?». Перепутать их — самая частая ошибка дня, о ней на следующей странице. <=и>=— два символа, знак=второй.=>и=<— ошибка. Знак≥, скопированный из текста или таблицы, — тоже ошибка.- Сравнение ничего не меняет.
stock >= orderedтолько читает ячейки, какPrintln.
Главное про границы: >= включает само число, > — нет. При 25 штуках на складе и 25 в заказе stock >= ordered — true, а stock > ordered — false. На этой разнице держится половина ошибок в скидках и порогах; подробно — на третьей странице.
Попробуйте сейчас: первые сравнения в scratch.
Цель: предсказать на бумаге, что напечатают сравнения, и сверить с запуском.
1. Сотрите заготовку в
scratch/main.goи наберите программу про остаток и заказ:✎ Наберите в файлscratch/main.gopackage 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.gopackage 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)falsefloat64 хранит 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.gopackage 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