День 02 · Переменные и присваивание · страница 3 из 6

Присваивание

Переменную объявили — ячейка появилась. Теперь то, ради чего она нужна: положить в неё новое значение. Это действие называется присваиванием, и в одном знаке = прячется больше, чем кажется.


= заменяет значение

Ситуация. Утром на складе 40 штук, днём пересчитали — 55.

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

import "fmt"

func main() {
	stock := 40
	fmt.Println("Утром:", stock)
	stock = 55
	fmt.Println("Днём:", stock)
}

Что увидите.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Утром: 40
Днём: 55

Как это читать. stock = 55 — «положи 55 в ячейку stock». В ячейке помещается одно значение, поэтому старое 40 при записи пропадает:

Образец · набирать не нужно
после строки 6          после строки 8
      ┌────┐                  ┌────┐
stock │ 40 │            stock │ 55 │
      └────┘                  └────┘

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


= — это не равенство

В математике x = x + 1 — неверное равенство. В программе такая строка встречается постоянно, потому что = — не утверждение «равны», а команда: «вычисли то, что справа, и положи результат в ячейку слева». Вслух удобно говорить «stock получает stock плюс 15».

stock = stock + 15, если в stock было 40:

  1. Вычислить правую часть: прочитать stock — 40, прибавить 15 — 55.
  2. Записать 55 в stock. Сорок пропало.

Правая часть вычисляется целиком до записи: пока считается stock + 15, в ячейке ещё старое значение.

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

import "fmt"

func main() {
	stock := 40
	fmt.Println("Утром:", stock)
	stock = stock + 15
	fmt.Println("После поставки:", stock)
	stock = stock - 12
	fmt.Println("После продажи:", stock)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Утром: 40
После поставки: 55
После продажи: 43
Строка Код Правая часть stock после строки
6 stock := 40 40 40
8 stock = stock + 15 40 + 15 → 55 55
10 stock = stock - 12 55 - 12 → 43 43

В строке 10 справа уже 55: каждая строка работает с тем, что оставила в ячейке предыдущая.


Попробуйте сейчас: предскажите четыре остатка.

Цель: проверить, что вы считаете присваивания так же, как программа.

  1. В scratch/main.go: stock сначала 100, потом получает stock - 30, потом stock + 7, потом stock - stock. Печать после каждого шага.
  2. До запуска запишите на бумаге четыре числа.
  3. Запустите и сравните:
▶ Выполните
cd ~/gocourse/day02/scratch
go run .

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


Сокращения: +=, -=, ++, --

Запись То же самое, что
stock += 15 stock = stock + 15
stock -= 12 stock = stock - 12
stock++ stock = stock + 1
stock-- stock = stock - 1

+= — один знак из двух символов, пробела между ними нет.

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

import "fmt"

func main() {
	stock := 40
	stock += 15
	stock -= 12
	stock++
	stock--
	stock--
	fmt.Println("Остаток:", stock)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Остаток: 42

40 → 55 → 43 → 44 → 43 → 42.

++ и -- в Go — отдельные команды. В C, Java, JavaScript x++ можно вставить внутрь печати или присваивания. В Go stock++ всегда стоит на своей строке. «Увеличить и напечатать» — это две строки.


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

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

Не запуская:

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

import "fmt"

func main() {
	boxes := 3
	boxes = boxes + 1
	boxes = boxes + 1
	boxes += boxes
	fmt.Println("Коробок:", boxes)
}

Держите в голове одну ячейку boxes и проходите строки по одной. Отправьте число, которое напечатает программа:

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

Вместо ВАШ_ОТВЕТ — это число.

После ответа проверьте себя запуском. Теперь ту же программу нужно набрать — в scratch/main.go:

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

import "fmt"

func main() {
	boxes := 3
	boxes = boxes + 1
	boxes = boxes + 1
	boxes += boxes
	fmt.Println("Коробок:", boxes)
}
▶ Выполните
cd ~/gocourse/day02/scratch
go run .

Если вывод разошёлся с вашим ответом, отправьте ответ заново — засчитывается последний.

Готово, когда: ответ отправлен — пункт проверки q1.


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

Опечатка, которую компилятор пропускает

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

import "fmt"

func main() {
	stock := 40
	stock =+ 15
	fmt.Println("Остаток:", stock)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Остаток: 15

Ждали 55, получили 15, компилятор молчит. =+ 15 для него — правильная запись = +15: «положить плюс пятнадцать». Программа собирается, но считает не то. Такое ловит gofmt — после Ctrl+S строка станет stock = +15, и лишний плюс будет виден, — и трассировка, о которой следующая страница.

Что видите Что это значит Что делать
Остаток: 15 вместо 55, компилятор молчит stock =+ 15 вместо stock += 15 += — плюс перед знаком равно
stock + 15 (value of type int) is not used сумму посчитали и никуда не положили stock += 15
cannot assign to 55 (neither addressable nor a map index expression) 55 = stock: слева число, а у числа нет ячейки слева всегда имя: stock = 55
syntax error: unexpected ++ in argument list; possibly missing comma or ) stock++ внутри Println stock++ отдельной строкой, печать — следующей
syntax error: unexpected ++ at end of statement next := stock++ сначала stock++, потом next := stock
syntax error: unexpected ++, expected } ++stock stock++
печатается старое значение Println стоит выше строки, которая меняет переменную сначала изменить, потом печатать

neither addressable nor a map index expression — «ни адресуемое, ни обращение к элементу map»: так компилятор перечисляет всё, чему присвоить значение можно (map — таблица по ключу, она в блоке 4). Важна первая половина сообщения, cannot assign to: слева стоит то, чему присваивать нельзя.


Попробуйте сейчас: задание stock.

Цель: stock/main.go печатает остаток после каждого события из TASK.txt, и считает эти остатки сама программа.

Задание stock — остаток товара за день, числа у каждого свои.

  1. Прочитайте условие:
▶ Выполните
cd ~/gocourse/day02/stock
cat TASK.txt
  1. В stock/main.go одна переменная для остатка — назовите её, например, stock: объявили — напечатали, дальше на каждое событие строка, которая её меняет, и строка печати. В строке печати стоит stock, а не число: промежуточные остатки не считайте на калькуляторе. Проверка ищет эти числа в коде и, если найдёт, пункт не засчитает; а ещё запускает копию вашей программы с другими числами событий и сверяет пересчитанный вывод.
  2. Запустите программу и проверьте формат:
▶ Выполните
go run .
gofmt -l .

Сравните вывод с TASK.txt построчно. gofmt -l . должен ничего не напечатать.

Готово, когда: вывод совпал с TASK.txt строка в строку, а gofmt -l . ничего не напечатал — пункты проверки stock, stock_fmt, t_gofmt.


Дальше: как выполнить программу в голове — course next