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