Константы и имена
Последняя страница дня — про значения, которые менять нельзя, и про то, как называть переменные, чтобы код собирался и читался.
Константа — имя для значения, которое не меняется
Ситуация. Доставка стоит 29900 копеек. Число участвует в расчёте каждого заказа. Напишете его в пяти местах — через месяц никто не вспомнит, что это за число, а при смене тарифа одно место забудут.
package main
import "fmt"
const deliveryPrice = 29900
func main() {
itemsPrice := 154000
total := itemsPrice + deliveryPrice
fmt.Println("Товары, коп.:", itemsPrice)
fmt.Println("Доставка, коп.:", deliveryPrice)
fmt.Println("Итого, коп.:", total)
}Что увидите.
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Товары, коп.: 154000
Доставка, коп.: 29900
Итого, коп.: 183900Как это читать. const deliveryPrice = 29900 — константа: имя для значения, которое известно до запуска и не меняется, пока программа работает. Тип выводится из значения. Имя объясняет смысл числа, а само число задано в одном месте. Константы, нужные всей программе, объявляют над func main.
| Переменная | Константа | |
|---|---|---|
| объявление | var или := |
const |
| поменять значение | можно | нельзя — ошибка компиляции |
| какое значение | любое, в том числе посчитанное во время работы | только известное до запуска |
| объявили и не используете | ошибка declared and not used |
не ошибка |
Ещё примеры из нашей области:
const vatPercent = 22
const maxItemsPerOrder = 50
const warehouseName = "Северный"Константу нельзя изменить
package main
import "fmt"
const deliveryPrice = 29900
func main() {
deliveryPrice = 0
fmt.Println("Доставка, коп.:", deliveryPrice)
}stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:8:2: cannot assign to deliveryPrice (neither addressable nor a map index expression)Как это читать. Сообщение то же, что у 55 = stock на третьей странице, и читается так же: важна первая половина, cannot assign to — слева стоит то, чему присваивать нельзя. Константа — не ячейка: компилятор подставляет её значение прямо туда, где она упомянута, как если бы там было написано 29900. Записать некуда. deliveryPrice++ даёт то же сообщение.
Нужно значение, которое начинается с константы, а дальше меняется, — скопируйте константу в переменную: price := deliveryPrice. Имя у новой переменной своё, а не имя константы: deliveryPrice := deliveryPrice - 100 собирается, но константу не освобождает — рядом встаёт вторая переменная с тем же именем, и внутри функции видна только она. Другие ошибки с константами — в таблице в конце страницы.
Попробуйте сейчас: почините
fix3, не трогая константу.Цель:
fix3собирается и печатает цену и цену со скидкой, а строка сconstосталась как была.В
fix3цена товара — константа, а программа пытается её изменить.
- Запустите и прочитайте сообщение:
▶ Выполнитеcd ~/gocourse/day02/fix3 go run .
- Строку с
constне трогайте: меняется не цена товара, а то, что платит покупатель. Для цены со скидкой заведите свою переменную, и имя ей возьмите другое —finalилиtoPay. Два коротких пути проверка не засчитает:const→var(цена перестаёт быть константой) иprice := price - discount(вторая переменная с тем же именем). Изменить можно не больше двух строк.- Сообщение — в
~/errors.md.Готово, когда:
go run .печатает две строки из комментария в начале файла — пункт проверкиfix3.
Правила имён
Имя переменной или константы:
- состоит из букв, цифр и
_; - начинается с буквы или
_, не с цифры; - различает регистр:
stock,StockиSTOCK— три разных имени; - не совпадает с ключевым словом:
var,const,func,type,if,forи другими — полный список вcourse extra 02.
Больше всего сообщений даёт дефис. Переменную cellA набрали как cell-a:
package main
import "fmt"
func main() {
cell-a := 5
fmt.Println("В ячейке:", cell-a)
}stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:6:2: undefined: cell
./main.go:6:2: non-name cell - a on left side of :=
./main.go:6:7: undefined: a
./main.go:7:34: undefined: cell
./main.go:7:39: undefined: aКак это читать. Дефис — это минус. Компилятор читает строку 6 как cell - a := 5: слева от := стоит не имя, а выражение «cell минус a», отсюда non-name cell - a on left side of :=. Переменных cell и a нет, отсюда undefined — по сообщению на каждое упоминание в строках 6 и 7. Первым напечатано undefined: cell, а причина названа во втором сообщении: сообщения про одну строку читайте вместе. Остальные поломки имён — в таблице в конце страницы.
Соглашения — компилятор их не требует, но так пишут все:
- составные имена «верблюдом»:
deliveryPrice,oldPrice. Имяdelivery_priceсоберётся, но в Go так не пишут; - свои имена — с маленькой буквы. С большой начинаются имена, видимые из других пакетов, как
Println; подробно в блоке 5; - имя говорит, что лежит в ячейке:
stock, а неx.
Почему латиница
Go разрешает буквы любого алфавита:
package main
import "fmt"
func main() {
остаток := 40
fmt.Println("Остаток:", остаток)
}stagiaire@lab:~/gocourse/day02/scratch$ go run .
Остаток: 40И всё же в курсе имена пишутся латиницей. Главная причина — буквы-двойники: кириллические «с», «о», «р», «а», «е», «х» на экране не отличить от латинских, но для компилятора это разные символы. Здесь в Println одна буква набрана в русской раскладке:
package main
import "fmt"
func main() {
stock := 40
fmt.Println("Остаток:", stoсk)
}stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:6:2: declared and not used: stock
./main.go:7:33: undefined: stoсkИмена выглядят одинаково, а компилятор видит две разные переменные. Глазами такую ошибку не найти. Когда весь код латиницей — ключевые слова, пакеты, имена, — двойникам неоткуда взяться. К тому же чужой код, документация и коллеги из других стран — всё на латинице. Строки в кавычках и комментарии пишите по-русски: там кириллица на своём месте.
Попробуйте сейчас: сломайте имя тремя способами.
Цель: увидеть сообщения компилятора о неправильных именах и найти каждое в таблице ниже.
В
scratch/main.goсломайте имя переменной тремя способами:
- начните его с цифры;
- вставьте дефис;
- наберите одну букву в русской раскладке.
После каждой поломки — запуск, найти сообщения в таблице ниже, вернуть как было:
▶ Выполнитеcd ~/gocourse/day02/scratch go run .Готово, когда: для всех трёх поломок сообщения найдены в таблице, а программа снова собирается.
Что может пойти не так
Сообщения про имена в таблице — для main из двух строк: объявление переменной и Println с ней. Поэтому сообщений по два и больше.
| Что видите | Что это значит | Что делать |
|---|---|---|
cannot assign to deliveryPrice (neither addressable nor a map index expression) |
попытка изменить константу через =, += или ++ |
завести переменную с начальным значением из константы |
missing init expr for vatPercent |
const vatPercent без значения |
у константы значение обязательно |
itemsPrice + deliveryPrice (value of type int) is not constant |
const total = itemsPrice + deliveryPrice: в выражении переменная |
результат — в переменную: total := … |
syntax error: unexpected name cells at end of statement, ниже syntax error: unexpected name cells in argument list; possibly missing comma or ) |
имя с цифры, 2cells := 5: прочитано число 2 и лишнее слово |
начать имя с буквы: cells2 |
undefined: cell, ниже non-name cell - a on left side of := и ещё три строки undefined |
дефис в имени cell-a — это минус |
cellA |
syntax error: unexpected name price at end of statement, ниже syntax error: unexpected name price in argument list; possibly missing comma or ) |
пробел делит имя delivery price на два слова |
deliveryPrice |
syntax error: unexpected :=, expected name, ниже syntax error: unexpected keyword type, expected expression |
имя type — ключевое слово |
другое имя: itemType |
declared and not used: stock, ниже undefined: Stock |
объявлена stock, а в Println — Stock: регистр важен |
написать имя буква в букву |
declared and not used: stock, ниже undefined: stoсk, хотя имена на вид одинаковые |
в одном из имён буква набрана в русской раскладке | стереть имя и набрать заново в латинской раскладке |
Итог дня
| Запись | Что делает |
|---|---|
var stock int = 40 |
объявить переменную с типом и значением |
var sold int |
объявить с нулевым значением: 0 у int, "" у string |
stock := 40 |
объявить внутри функции, тип из значения |
stock = 55 |
заменить значение существующей переменной |
stock += 15, stock++ |
сначала правая часть по текущему значению, потом запись |
oldPrice := price |
скопировать значение; дальше переменные независимы |
cellA, cellB = cellB, cellA |
обмен: все правые части считаются до записи |
const deliveryPrice = 29900 |
значение, которое не меняется |
И приём на весь курс — таблица трассировки: значения после каждого шага, по коду, а не по смыслу.
Словарик ошибок
Формат строки тот же, что вчера: текст ошибки, тире с пробелами вокруг, что это значило. Строку без тире проверка не засчитает. Двоеточие не подходит: оно есть в каждом сообщении компилятора.
Попробуйте сейчас: словарик и проверка дня.
Цель: в
~/errors.mdне меньше пяти разборов, иcourse check 02показал итог дня.
- Откройте словарик:
▶ Выполнитеcode ~/errors.md
- К вчерашним разборам добавьте сегодняшние, всего минимум пять. Образец строки — один, дальше вторую половину пишете сами:
Образец · набирать не нужноdeclared and not used: left — посчитал значение в переменную и забыл её использовать no new variables on left side of := — cannot assign to price (neither addressable nor a map index expression) —Разбор пишите своими словами, про свой случай: списанная строка в словарике ничего не стоит — он нужен вам, а не проверке.
- Потом — итог и проверка:
▶ Выполнитеcourse lab 02 course check 02Готово, когда:
course check 02засчитал пунктerrors, а по остальным пунктам вы сверились сcourse lab 02.
Материал дня закончен. Задания — course lab 02, дополнительное чтение — course extra 02