Под конец данного урока вы сможете:
- Оценить преимущества области видимости переменных;
- Использовать краткий способ объявления переменных;
- Посмотреть, как область видимости переменных взаимодействует с
for,ifиswitch; - Выбрать узкую или широкую область видимости для рассматриваемого случая.
По ходу выполнения программы многие переменные немного используются на протяжении довольно короткого времени, после чего сбрасываются. Это обусловлено правилами видимости языка.
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
Содержание статьи
- Область видимости переменных в Golang
- Краткое объявление переменных в Go
- Локальная и глобальная область видимости
Как много информации человек может запомнить за один раз? Было сделано предположение, что наша кратковременная память ограничена семью объектами. Хорошим примером данной концепции является семизначный номер телефона.
Компьютеры могут хранить довольно много значений в своей краткосрочной памяти или Random-access memory (RAM), однако нужно помнить, что код читается не только компьютерами, но и людьми. Поэтому очень важно, чтобы код оставался максимально простым и понятным.
Если бы можно было изменить переменную в любое время или получить к ней доступ откуда бы то ни было, тогда большая программа очень быстро стала бы очень беспорядочной. Область видимости переменных помогает сфокусироваться на уместных для рассматриваемой функции или участка кода переменных без необходимости заботиться об оставшейся части.
Область видимости переменных в Golang
Когда переменная объявляется, она попадает в область видимости. Проще говоря, переменная становится видимой. Если переменная находится в области видимости, тогда программа всегда может получить к ней доступ. Однако, как только переменная покидает область видимости, при попытке получить к ней доступ выйдет ошибка.
Преимущество области видимости в том, что вы можете повторно использовать одно и то же название для разных переменных. Представьте программу, в которой у каждой переменной должно быть уникальное название. Теперь попробуйте представить программу в несколько раз больше.
При чтении кода область видимости очень помогает, потому что пропадает необходимость запоминать все переменные. Как только переменная выходит из области видимости, можно больше не думать о ней.
В Go область видимости начинает и заканчивается фигурными скобками {}. В следующей программе функция main начинает область видимости, а вместе с циклом for стартует вложенная область.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package main import ( "fmt" "math/rand" ) func main() { var count = 0 for count < 10 { // Начало области видимости var num = rand.Intn(10) + 1 fmt.Println(num) count++ } // Конец области видимости } |
Переменная count объявляется внутри области видимости функции, она видима до конца функции main, в то время как переменная num объявляется внутри области видимости цикла for. По завершении цикла переменная num выходит из области видимости.
При попытке получить доступ к переменной num после цикла, компилятор Go выведет ошибку. Однако получить доступ к переменной count по завершении цикла for все еще можно, ведь ее объявили за пределами цикла, хотя особой причины для этого не было. Для заключения переменной count в области видимости цикла понадобится использовать другой способ объявления переменных Go.
Вопросы для проверки:
- В чем преимущество области видимости переменных?
- Что случится с переменной, когда она покинет область видимости? Измените код предыдущего примера кода для получения доступа к
numпосле цикла и посмотрите, что произойдет.
Ответы на вопросы
- Переменные с одинаковым названием могут использоваться в нескольких местах, не вызывая при этом конфликтов. В таком случае можно заниматься только теми переменными, что на данный момент находятся в области видимости.
- Переменная будет больше не видна и не доступна. Компилятор Go выведет ошибку
undefined: num error.
Краткое объявление переменных в Go
Краткое объявление переменной осуществляется через альтернативный синтаксис для ключевого слова var. Следующие две строки эквивалентны:
|
1 2 |
var count = 10 count := 10 |
Поначалу может показаться, что разница невелика, однако разница в три символа делает сокращенный вариант намного популярнее способа с var. Кроме того, краткое объявление может использоваться в некоторых местах, где недопустимо ключевое слово var.
В следующей программе показан пример цикла for, что совмещает инициализацию, условие и последующий оператор, что уменьшает значение count. При использовании данной формы цикла for очень важен порядок: инициализация, условие, операция.
|
1 2 3 4 5 6 7 |
var count = 0 for count = 10; count > 0; count-- { fmt.Println(count) } fmt.Println(count) // count остается в области видимости |
Не используя краткое объявление, переменную count нужно было бы объявить за пределами цикла, следовательно, переменная в таком случае после завершения цикла остается в области видимости.
В следующей программе при задействовании краткого объявления, переменная count объявляется и инициализируется как часть цикла for, по завершении цикла выходит из области видимости. Если бы к переменной count доступ был получен за пределами цикла, тогда компилятор Go выдал бы ошибку undefined: count.
|
1 2 3 |
for count := 10; count > 0; count-- { fmt.Println(count) } // count больше не в области видимости |
Краткое объявление дает возможность объявить новую переменную в операторе if. В следующем коде переменная num может использовать в любом ответвлении оператора if.
|
1 2 3 4 5 6 7 |
if num := rand.Intn(3); num == 0 { fmt.Println("Space Adventures") } else if num == 1 { fmt.Println("SpaceX") } else { fmt.Println("Virgin Galactic") } num больше не в области видимости |
Краткое объявление может использоваться с оператором switch, как показано в следующей программе:
|
1 2 3 4 5 6 7 8 9 10 |
switch num := rand.Intn(10); num { case 0: fmt.Println("Space Adventures") case 1: fmt.Println("SpaceX") case 2: fmt.Println("Virgin Galactic") default: fmt.Println("Random spaceline #", num) } |
Вопрос для проверки:
Как бы изменилась область видимости переменной num, если бы в предыдущих примерах не использовалось краткое объявление?
Ответ на вопрос
С помощью var объявить переменную сразу после ключевых слов if, switch и for невозможно. В таком случае num требуется объявить перед операторами if/switch, чтобы переменная num осталась в области видимости за пределами окончания if/switch.
Локальная и глобальная область видимости
Следующий код генерирует и отображает случайную дату — к примеру, дату вылета на Марс. В нем также представлено несколько областей видимости и показано, почему особенно важно задуматься об области видимости во время объявления переменной.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package main import ( "fmt" "math/rand" ) var era = "AD" // переменная era доступна через пакет func main() { year := 2018 // переменные era и year находятся в области видимости switch month := rand.Intn(12) + 1; month { // переменные era, year и month в области видимости case 2: day := rand.Intn(28) + 1 // новый день fmt.Println(era, year, month, day) case 4, 6, 9, 11: day := rand.Intn(30) + 1 fmt.Println(era, year, month, day) default: day := rand.Intn(31) + 1 fmt.Println(era, year, month, day) } // month и day за пределами области видимости } // year за пределами области видимости |
Переменная era объявляется за пределами функции main области видимости пакета. Если бы в пакете main использовалось несколько функций, тогда переменная era была бы видна для них всех.
На заметку: Краткое объявление недоступно для переменных, объявленных в области видимости пакета, поэтому переменную
eraнельзя объявить черезera := "AD"в ее текущей позиции.
Переменная year видна только внутри функции main. Другие функции видят era, но не year. Область видимости функции уже, чем область видимости пакета. Она начинается c ключевого слова func и заканчивается закрывающей скобкой.
Переменная month доступна внутри оператора switch, но как только оператор switch заканчивается, month выводится из области видимости. Область видимости начинается с ключевого слова switch и заканчивается закрывающей скобкой switch.
У каждого case есть собственная область видимости и три независимые переменные day. Как только каждый случай заканчивается, объявленная внутри case переменная day выходит за пределы области видимости. Это единственная ситуация, когда для обозначения области видимости не используются скобки.
Код примера не идеален. Узкая область видимости переменных month и day приводит к дубликату кода (Println, Println, Println). Когда код дублируется, кто-то может пересмотреть код в одной области, но не в другой (при решений не выводить era, но забыв изменить один case). Иногда дублированный код имеет смысл, однако чаше всего он рассматривается как код с запашком и указывается на возможные проблемы в программе.
Для удаления дубликатов и упрощения кода переменные нужно было объявлять в более широкой области видимости функции, делая их доступными после оператора switch для дальнейшей работы.
Время рефакторинга! Рефакторинг предполагает модификацию кода без изменения его поведения. Следующая программа по-прежнему выводит случайную дату.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package main import ( "fmt" "math/rand" ) var era = "AD" func main() { year := 2018 month := rand.Intn(12) + 1 daysInMonth := 31 switch month { case 2: daysInMonth = 28 case 4, 6, 9, 11: daysInMonth = 30 } day := rand.Intn(daysInMonth) + 1 fmt.Println(era, year, month, day) } |
Хотя узкая область видимости зачастую сокращает ментальную работу, код выше демонстрирует, что слишком сильное ограничение переменных может привести к менее читабельному коду. Учитывайте это и делайте рефакторинг до тех пор, пока код не станет максимально простым.
Вопрос для проверки:
Как можно распознать слишком узкую область видимости переменных?
Ответ на вопрос
Код дублируется из-за места объявления переменных.
Заключение
- Открывающая фигурная скобка
{вводит новую область видимости, что оканчивается закрывающей скобкой}; - Ключевые слова
caseиdefaultтакже вводят новую область видимости, хотя фигурные скобки здесь уже не используются; - Место объявления переменной определяется тем, в какой она области видимости;
- Переменные, объявленные на той же строке, что и ключевые слова
for,ifилиswitchнаходятся в области видимости до окончания данного оператора; - Иногда широкая область видимости лучше, а иногда — узкая, все зависит от ситуации.
Задание для итоговой проверки:
Измените следующую программу для обработки високосных годов. Код должен:
- Генерировать случайный год вместо постоянного использования 2018;
- Для февраля присвойте
daysInMonthна 29 для високосных годов, и 28 для всех остальных. Можете использовать операторifвместо блокаcase; - Используйте цикл
forдля генерации и отображения 10 случайных дат.
Программа для модификации:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package main import ( "fmt" "math/rand" ) var era = "AD" func main() { year := 2018 month := rand.Intn(12) + 1 daysInMonth := 31 switch month { case 2: daysInMonth = 28 case 4, 6, 9, 11: daysInMonth = 30 } day := rand.Intn(daysInMonth) + 1 fmt.Println(era, year, month, day) } |
Решение
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package main import ( "fmt" "math/rand" ) var era = "AD" func main() { for count := 0; count < 10; count++ { year := 2018 + rand.Intn(10) leap := year%400 == 0 || (year%4 == 0 && year%100 != 0) month := rand.Intn(12) + 1 daysInMonth := 31 switch month { case 2: daysInMonth = 28 if leap { daysInMonth = 29 } case 4, 6, 9, 11: daysInMonth = 30 } day := rand.Intn(daysInMonth) + 1 fmt.Println(era, year, month, day) } } |

Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
Сделал так, но потом решил сравнить, и понял что надо было сильнее сократить код