Skip to content

Функции первого класса, замыкания и анонимные функции в Go

В этой теме мы разберем:

  • функции первого класса;

  • присваивание функции переменной в Go;

  • передачу функции другой функции в Go;

  • объявление типов функции в Go;

  • замыкания (Closures) и анонимные функции в Go.

Функции первого класса

В Go функции можно присвоить переменным, передать функции другим функциям или даже написать функции для возвращения функций. Функции первого класса работают там же, где типы integer, string и прочие.

В этой теме мы рассмотрим потенциал использования функций первого класса как элементов теоретической программы «Станции экологического мониторинга Ровер (REMS)», что считывает данные температурных сенсоров.

Для сравнения подумаем о примере из реального мира. Мясо будет вкуснее с кетчупом. После приготовления мясного блюда ты можешь найти домашний рецепт кетчупа, но зачем все так усложнять — сходи за кетчупом в обычный магазин.

Функции первого класса можно сравнить с мясом, которому нужно добавить кетчупа. Представим код, в котором функции makeMeat нужно вызвать функцию для кетчупа, будь то makeKetchup или openKetchup. Функции для кетчупа также можно использовать отдельно, однако мясо без кетчупа будет не таким вкусным.

Помимо рецептов и сенсоров температуры, какие другие примеры изменения функции с помощью другой функции ты можешь привести?

Присваивание функции переменной в Go

Сенсоры станции погоды предоставляют данные о температуре воздуха в диапазоне 150–300° K. У нас есть функции для конвертации градусов Кельвина в другие единицы измерения при наличии данных, однако при отсутствии специального сенсора, встроенного в компьютер (или Raspberry Pi) и считывающего информацию, это может стать проблематично.

Пока мы можем использовать фальшивый сенсор, что будет возвращать псевдослучайные числа, однако в таком случае нужно будет найти способ использовать realSensor или fakeSensor взаимозаменяемо. В следующем примере именно это и происходит. При создании такой программы различные реальные сенсоры также могут быть подключены, к примеру, для сбора данных как о температуре земли, так и о температуре воздуха.

Самое время протестировать код!

В этом примере переменная sensor присваивает функцию fakeSensor, но не сам результат вызова функции. Вызовы функции и метода всегда со скобками вроде fakeSensor(), но здесь другой случай.

Теперь вызов sensor() сразу вызовет realSensor или fakeSensor в зависимости от того, к какой функции присваивается sensor.

Переменная sensor является типом той функции, что не принимает параметров и возвращает результат типа kelvin. Если не полагаться на автоматическое присваивание типа, переменная сенсора может быть объявлена следующим образом.

В коде ты можешь заново назначить sensor к realSensor из-за совпадения с сигнатурой функции fakeSensor. У обеих функций одинаковое количество параметров с тем же типом, а так же одинаковый возвращаемый тип, то есть сигнатуры функций совпадают.

Передача функции другой функции в Golang

Переменные могут отсылаться к функциям, а также передаваться функциям. Это значит, что Go допускает передачу одних функций другим.

Для фиксирования данных о температуре каждую секунду в Листинге 2 объявляется новая функция measureTemperature, что принимает функцию сенсора в качестве параметра. Она периодически вызывает функцию сенсора, будь то fakeSensor или realSensor.

Возможность передачи функций представляет собой мощный способ, что позволяет поделить код на части. Без учета функций первого класса мы бы наверняка пришли к функциям measureRealTemperature и measureFakeTemperature, что содержат идентичный код.

Самое время протестировать код!

Функция measureTemperature принимает два параметра, второй параметр принадлежит к типу func() kelvin. Объявление выглядит как объявление переменной того же типа.

Функция main может передать название функции к measureTemperature.

Объявление типов функции в Golang

В Go есть возможность объявления нового типа для функции, что позволяет сократить и уточнить код, к которому она относится. Мы использовали тип kelvin скорее для передачи единицы измерения температуры, чем для базового представления. То же самое может быть сделано для переданных функций.

В коде речь идет не о функции, что не принимает параметров, возвращая значение kelvin, а о функциях sensor. Этот тип можно использовать для сокращения кода. Таким образом следующее объявление:

Можно переписать подобным образом:

В этом примере сложно увидеть улучшение, ведь теперь при просмотре строки кода нужно понимать, что собой представляет sensor. Однако если бы sensor использовался в нескольких местах, или если бы у типа функции было несколько параметров, использование типа избавило бы от беспорядка.