Это седьмая статья из моей еженедельной серии 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, которая может сделать ваш код более модульным, удобочитаемым и масштабируемым. Далее я расскажу об рекурсии и о том, как применить эти принципы к вашим функциям. Не могу дождаться, тогда увидимся!