Я не знаю ML, поэтому мой ответ будет в Scheme, который является другим функциональным языком программирования. Я предполагаю, что вы не знаете Scheme, и хорошо прокомментируете мой код.
** Примечание. Схема типизируется динамически, поэтому вы не увидите никаких объявлений типов (например, int, float, char и т. Д.).
(define (double x) ;this defines a new function called "double" which takes one argument called x
(* 2 x)) ;return 2 times its argument. Function application in Scheme takes the form (function arg1 arg2 ...)
Мы будем использовать эту вновь определенную функцию следующим образом:
(double 10) ;returns 20
(double 8) ;returns 16
В этой функции переменная x
является локальной переменной . x
является формальным параметром double. Всякий раз, когда мы используем x
в теле double, нет никаких сомнений относительно того, какое значение мы имеем в виду. Но что по этому поводу:
(define (foo x) ;define a function "foo" which takes on argument called x
(* x a)) ;return x times a
Еще раз, x
является формальным параметром. Но как насчет a
? Что мы имеем в виду, когда используем a
в теле foo? a
не определен в foo, поэтому он называется свободной переменной . Чтобы увидеть, что означает a
, нам нужно взглянуть за пределы foo. Например, предположим, что foo был определен в этом контексте:
(define (bar a) ;define a function "bar" which takes one argument called "a"
(define (foo x) ;define "foo" as an inner function of bar
(* x a))) ;foo returns x * a
Теперь мы знаем, что означает a
. Схема (а также ML) лексически ограничена , что означает, что, чтобы узнать, что означает переменная a
, мы смотрим на текстовый контекст (надеюсь, это имеет смысл).
Теперь вышеприведенное определение bar
на самом деле неверно: даже мысль foo
что-то возвращает, bar
ничего не возвращает. Он определяет foo
как внутреннюю функцию, но после этого определения нет никаких утверждений. Давайте исправим это:
(define (bar a) ;define a function "bar" which takes one argument called "a"
(define (foo x) ;define "foo" as an inner function of bar
(* x a)) ;foo returns x * a
foo) ;bar returns the function foo
Теперь bar
возвращает функцию foo
. Мы только что превратили bar
в функцию высшего порядка , потому что она возвращает другую функцию.
Но есть проблема. Обычно, когда возвращается bar
, локальная переменная a
больше не нужна и ее значение теряется. Но foo
все еще ссылается на a
! Так что a
нужно немного подождать. Вот здесь и возникают замыкания. Значение a
«закрыто», поэтому оно остается неизменным, пока существует функция foo
. Таким образом, мы можем вызвать foo
даже после того, как bar
завершит выполнение. Теперь давайте переименуем foo
и bar
, чтобы понять, почему это полезно:
(define (make-multiplier a)
(define (multiplier x)
(* x a))
multiplier)
Теперь мы можем сделать это:
(define triple (make-multiplier 3)) ;call make-multiplier with the value 3. Bind the function which is returned to the variable "triple."
(triple 5) ;call triple with the value 5. Since the 3 was "closed over", (triple 5) returns 5 * 3, which is 15.
Итак - когда функция имеет «свободную переменную», для функции создается замыкание, которое «закрывает» свободную переменную и сохраняет ее в течение времени жизни функции. Таким образом, когда функция передается и покидает контекст, в котором она была определена, «свободная переменная» продолжает оставаться действительной.