Это седьмая статья из моей еженедельной серии Learning Go. На прошлой неделе я обсуждал объявления функций, аргументы, параметры и анонимные функции. На этой неделе я буду говорить о функциональных литералах и замыканиях.
Функциональные литералы (функциональные выражения)
Функциональные литералы могут быть назначены переменной или вызваны (вызваны) напрямую. Они могут ссылаться на переменные, определенные в функции окружения, что делает их замыканием (подробнее об этом мы поговорим позже в этом посте).
Итак, в чем разница между объявлением функции и литералом функции?
Объявление функциипривязывает идентификатор (имя функции) к функции. Вы можете вызвать эту функцию, используя ее идентификатор.
Функциональный литерал — это замыкание, означающее, что они могут ссылаться на переменные, которые были определены в окружающей функции. Эти переменные могут совместно использоваться литералом function и окружающей функцией. Эти переменные сохраняются до тех пор, пока они доступны.
Давайте начнем с простого примера и постепенно усложняем его.
- внутри
func
main
мы объявляем переменнуюf
и назначаем анонимную функцию - когда эта функция вызывается, она использует пакет
fmt
для печатиstring
I am a function literal!
- мы вызываем этот функциональный литерал так же, как мы вызываем объявления функций: идентификатор, за которым следуют аргументы, заключенные в круглые скобки
()
- этот литерал функции не требует параметров; поэтому мы не передаем никаких аргументов
- после вызова
f
печатаетсяI am a function literal
и программа завершает работу.
Давайте рассмотрим пример, когда функциональный литерал имеет параметр:
- внутри
func
main
мы объявляем переменнуюf
и назначаем ее анонимной функции, которая принимает один параметр,x
, типаint
- используя пакет
fmt
, мы печатаемstring
my birth year is
, за которым следует значениеx
- обратите внимание, что при вызове
f
мы передаем один аргумент1990
f
печатаетmy birth year is 1990
и программа завершает работу
Далее давайте посмотрим, как мы можем вернуть function
из функционального литерала:
bar
:
- ниже
func
main
, используя ключевое словоfunc
, мы создаем объявление функции с идентификаторомbar
с двумя типами возврата:func()
иint
- эти типы возвращаемых значений сообщают нам, что
bar
должен возвращать функцию иint
внутри этой функции - внутри тела функции
bar
мыreturn
анонимная функция, которая имеет тип возвращаемого значенияint
- внутри этой анонимной функции мы возвращаем значение
2020
типаint
main
:
- внутри
func
main
мы объявляем переменнуюf
и присваиваем ей возвращаемое значение объявления функцииbar
- примечание:
f
назначается возвращаемому значению, потому что мы вызываемbar
; поэтомуbar
возвращает значение, котороеf
хранит в памяти. В данном случае это возвращаемое значение является функцией. f
вызывается на следующей строке внутри функцииPrintln
из пакетаfmt
- возвращаемое значение функции
bar
— это функция, которая возвращает значение2020
типаint
: следовательно,f()
напечатает2020
Как видно из нескольких из этих примеров, функциональные литералы могут быть очень мощными и могут очень динамично использоваться в вашем коде. Помните несколько вещей, когда думаете об использовании функционального литерала вместо объявления функции:
- это анонимные функции
- переменные являются общими для функционального литерала и окружающей функции (замыкание)
- переменные «выживают», пока они все еще доступны
Закрытие
способ, которым анонимная функция ссылается на переменные, объявленные вне самой анонимной функции
Немного мозговой изгиб, да?
Концепция замыкания может показаться очень абстрактной, что затрудняет понимание того, как они работают, и проблем, которые они решают.
Я уверен, что увидеть замыкание в действии — лучший способ узнать, как они работают:
incrementor
:
- сначала мы создаем
incrementor
, это должно быть похоже наbar
в последнем разделе incrementor
– это объявление функции, которое возвращает функцию иint
внутри этой функции.- используя ключевое слово
var
мы объявляем переменнуюx
типаint
x
не присваивается значение; поэтому ему присваиваетсяzero value
(0
)- затем мы возвращаем анонимную функцию, которая, как ожидается, будет
return
значением типаint
- обратите внимание, используя оператор
++
, мы увеличиваем значениеx
на1
— как это возможно? ответ закрытие - после увеличения
x
мы возвращаемx
main
:
- внутри
func
main
мы создаем переменнуюa
и присваиваем ей возвращаемое значениеincrementor()
- в следующей строке
a
вызывается внутри функцииPrintln
из пакетаfmt
- поскольку возвращаемое значение функции
a
является анонимной функцией внутриincrementor()
, мы увеличиваемx
на1
и возвращаем значение1
; поэтому печатается1
- мы повторяем этот процесс, снова вызывая
a
внутри функцииPrintln
- поскольку мы уже вызвали
a
, значениеx
равно1
; поэтому, когда мы увеличиваемx
, возвращаемое и печатаемое значение будет2
Обратите внимание, когда мы присваиваем incrementor()
переменной b
, она не возвращает 3
, почему?
Хотя a
и b
было назначено одно и то же возвращаемое значение для incrementor
, b
вызывалось только один раз; поэтому он содержит собственное уникальное значение 1
.
В этом сила замыкания, изоляции данных. Теперь вы можете легко использовать общие действия для нескольких переменных, и эти переменные могут иметь свои собственные уникальные значения.
Вкратце
Надеюсь, вам понравилось изучение функциональных литералов и замыканий. Благодаря силе замыкания вы получаете еще одну мощную функцию языка программирования Go, которая может сделать ваш код более модульным, удобочитаемым и масштабируемым. Далее я расскажу об рекурсии и о том, как применить эти принципы к вашим функциям. Не могу дождаться, тогда увидимся!