Замыкания — одна из самых мощных возможностей в Лиспексе. Это механизм, который позволяет функции «запоминать» окружение, в котором она была создана. Это руководство проведет вас через то, что такое замыкание и почему оно так полезно.
Что такое замыкание?
Простыми словами, замыкание — это функция, объединенная со своим «рюкзаком». Этот рюкзак содержит все переменные, которые были в области видимости в момент создания функции.
Это означает, что функция может получить доступ к этим «запомненным» переменным, даже если она вызывается из совершенно другой части вашей программы.
Давайте рассмотрим это на самом распространенном паттерне: «фабрика функций».
Паттерн «Фабрика функций»
Представьте, что вам нужно много функций, которые делают похожие вещи. Например, одна функция, которая добавляет 5 к числу, другая, которая добавляет 10, и еще одна, которая добавляет 100. Вместо того чтобы писать каждую вручную, вы можете создать «фабрику», которая будет производить эти функции для вас.
Шаг 1: Напишите фабричную функцию
Наша фабрика, make-adder, будет принимать один аргумент, amount-to-add, и ее задача — возвращать новую функцию.
(define (make-adder amount-to-add)
(lambda (n)
(+ n amount-to-add)))
Шаг 2: «Магия» замыкания
Когда мы вызываем (make-adder 5), вот что происходит:
- Для вызова
make-adderсоздается новое, временное окружение. - Внутри этого окружения параметр
amount-to-addпривязывается к значению5. - Создается функция
lambda. При своем создании она «осматривается» и упаковывает все переменные, которые видит, в свой «рюкзак». Она видит, чтоamount-to-addравен5, и сохраняет эту информацию. make-adderзавершает работу и возвращает функциюlambdaвместе с ее рюкзаком, содержащимamount-to-add = 5.
Эта возвращенная функция — лямбда плюс ее рюкзак — и есть замыкание.
Шаг 3: Используйте созданные функции
Теперь мы можем создавать специализированные функции с помощью нашей фабрики:
;; Создаем функцию, которая всегда будет добавлять 5.
(define add-five (make-adder 5))
;; Создаем функцию, которая всегда будет добавлять 10.
(define add-ten (make-adder 10))
;; Давайте их используем!
(add-five 100)
;; Результат: 105
;; Функция `add-five` заглядывает в свой «рюкзак» и вспоминает, что
;; `amount-to-add` был равен 5.
(add-ten 100)
;; Результат: 110
;; У функции `add-ten` есть свой собственный, отдельный рюкзак, где
;; `amount-to-add` был равен 10.
Почему это полезно?
Этот паттерн невероятно мощный. Он позволяет вам создавать на лету настраиваемые, предварительно сконфигурированные функции. Вы можете использовать его для:
- Создания настраиваемых обработчиков событий.
- Настройки функций с начальным состоянием.
- Продвинутых паттернов, таких как каррирование и частичное применение.
Заключение
Понимание замыканий — ключ к освоению функционального программирования в Лиспексе. Просто помните: это функция + ее окружение при создании (рюкзак). С этой концепцией вы сможете писать более элегантный, модульный и повторно используемый код.
Для более формального определения вы всегда можете вернуться на страницу концепции Функции и замыкания.