Замыкание (программирование)

Материал из Seo Wiki - Поисковая Оптимизация и Программирование
Перейти к навигацииПерейти к поиску

Замыкание (англ. closure) в программировании — процедура, которая ссылается на свободные переменные в своём лексическом контексте.

Замыкание, так же как и экземпляр объекта, есть способ представления функциональности и данных, связанных и упакованных вместе.

Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. В записи это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.

Замыкание связывает код функции с её лексическим окружением (местом, в котором она определена в коде). Лексические переменные замыкания отличаются от глобальных переменных тем, что они не занимают глобальное пространство имён. От переменных в объектах они отличаются тем, что привязаны к функциям, а не объектам.


Реализации замыкания в языках программирования

Scheme

Пример работы замыканий на Scheme: <source lang=scheme> (define (make-adder n)  ; возвращает замкнутое лямбда-выражение

 (lambda (x)                ; в котором x - связанная переменная,
   (+ x n)))                ; а n - свободная (захваченная из внешнего контекста)

(define add1 (make-adder 1)) ; делаем процедуру для прибавления 1 (add1 10)  ; печатает 11

(define sub1 (make-adder -1)); делаем процедуру для вычитания 1 (sub1 10)  ; печатает 9 </source>

Nemerle

Nemerle полностью поддерживает замыкания, так как функциональный подход для этого языка родной. Nemerle поддерживает замыкания для локальных функций:

def x = 2; def function(y) { x * y }

function(3); // => 6

лямбда-выражений:

def list1 = [1, 2, 3]; def x = 2; list1.Map(elem => elem * x); // => [2, 4, 6] частичного применения функций и операторов. Ниже приведён код аналогичный примеру выше, но вместо лямбды использующий частичное применение оператора «*»: def x = 2; def lst = [1, 2, 3]; lst.Map(_ * x); // => [2, 4, 6] во всех этих примерах происходит замыкание на локальную переменную 'x'.

C#

Анонимные методы в C# 2.0 могут замыкаться на локальный контекст: <source lang=csharp>

int[] ary = { 1, 2, 3 };
int x = 2;
Array.ConvertAll<int, int>(ary, delegate(int elem) { return elem * x; }); // { 2, 4, 6 }</source>

Этот пример аналогичен примеру на Nemerle за исключением того, что вместо списка используется массив. Функция Array.ConvertAll аналогична функции Map. Она преобразует один список/массив в другой, применяя для каждого элемента передаваемую ей в качестве параметра функцию. В C# 3.0 введены лямбда-выражения, которые делают синтаксис более кратким и выразительным. Они также поддерживают замыкания. Замыкания в C# 3.0 практически аналогичны анонимным функциям из C# 2.0, но более кратки. Вот тот же пример с применением лямбда-выражений в C# 3.0: <source lang=csharp>

var ary = { 1, 2, 3 };
var x = 2;
ary.Select(elem => elem * x;); // { 2, 4, 6 }</source>

Метод Select аналогичен методам Map и Array.ConvertAll за тем исключением, что он принимает и возвращает IEnumerable<T>.

Ruby

Некоторые языки, такие как Ruby, позволяют выбирать различные способы замыканий по отношению к оператору возврата return. Вот пример на Ruby:

<source lang="ruby">

  1. ruby

def foo

 f = Proc.new { return "return from foo from inside proc" }
 f.call # после вызова функции замыкания f осуществляется выход из foo
        # результатом работы функции foo является результат работы f замыкания
 return "return from foo" 

end

def bar

 f = lambda { return "return from lambda" }
 f.call # после вызова функции замыкания f опродолжается выполнение bar
 return "return from bar"

end

puts foo # печатает "return from foo from inside proc" puts bar # печатает "return from bar" </source>

И Proc.new так же как и -> в этом примере, это способы создания замыкания, но семантика замыканий различна по отношению к оператору return.

PHP

PHP имеет встроенную поддержку замыканий начиная с версии 5.3. Пример замыкания с одной параметрической и одной контекстной переменной: <source lang=php> function getAdder($x) {

   return function ($y) use ($x) {
       // or: lexical $x;
       return $x + $y;
   };

} </source> Для более ранних версий возможно использовать одноименный шаблон проектирования, который реализуется в библиотеке Николаса Нассара.

Java

Java релизует концепцию замыкания с помощью анонимных классов. Анонимный класс имеет доступ к полям класса, в лексическом контексте которого он определён, а так же к переменным с модификатором final в лексическом контексте метода.

<source lang=java>

class CalculationWindow extends JFrame {
    private JButton btnSave;
    ...

    public final void calculateInSeparateThread(final URI uri) {
        // Выражение "new Thread() { ... }" представляет собой пример анонимного класса.
        new Thread() {
            void run() {
                // Имеет доступ к финальным (final) переменным:
                calculate(uri);
                // Имеет доступ к приватным членам содержащего класса:
                btnSave.setEnabled(true);
            }
        }.start();
    }
}

</source>

Python

Пример с использованием замыканий и карринга: <source lang=python>

  1. Реализация с помощью именованных функций:

def taskerize(func_object):

   def unbound_closure(*args, **kwarg):
       def bound_closure():
           return func_object(*args, **kwarg)
       return bound_closure
   return unbound_closure
  1. Равносильная реализация с использованием lambda:

taskerize = lambda func_object: (

   lambda *args, **kwarg: (
       lambda: func_object(*args, **kwarg)
   )

)

@taskerize # применение декоратора равнозначно записи testfunc = taskerize(testfunc) после объявления функции. def testfunc(a, b, c):

   return a + b * c

f = testfunc(1, 2, 3) print f() # выведет 7 </source>

Пример простого замыкания: <source lang=python>

  1. Реализация с помощью именованных функций:

def make_adder(x):

   def adder(n):
       return x + n # захват переменной "x" из внешнего контекста
   return adder
  1. То же самое, но через безымянные функции:

make_adder = lambda x: (

   lambda n: (
       x + n
   )

)

f = make_adder(10) print f(5) # 15 print f(-1) # 9 </source>

Пример карринга: <source lang=python>

  1. Функция с кучей аргументов (26 шт.), делающая что-то невразумительное.

def longfunc(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z):

   print 'Меня вызвали с такими аргументами: ', a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
   return a + b * c - d / e + f / g - h * i - (j * (k - l) + m) + (n * o) / (p - q + r) + (s * (t + (u * (v + w)))) - (x * y * z)

def curry(func_object, *args):

   def innerfunc(*local_args):
       # в функции выполняется замыкание на args и func_object из внешнего контекста
       return func_object(*(args + local_args)) # а еще нам нужно прилепить в конец тех аргументов, что у нас были, новые
   return innerfunc
  1. По уже сложившейся традиции — то же самое, только лямбдами:

curry = lambda func_object, *args: (

   lambda *local_args: (
       func_object(
           *(args + local_args)
   )
   )

)

  1. "достраиваем" функцию, как пожелаем.

f1 = curry(longfunc, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100) f2 = curry(f1, 110, 120, 130, 140) f3 = curry(f2, 150, 160, 170, 180, 190, 200) f4 = curry(f3, 210)

  1. не обязательно использовать функцию, к которой был применен карринг, только один раз.

f5 = curry(f4, 220, 230, 240, 250, 260) # раз f5b = curry(f4, 220, 230, 240, 250) # два! f6b = curry(f5b, 260)

print f5() # выведет 2387403 print f6b() # опять выведет 2387403

  1. контроль того, что карринг всё сделал верно (вызываем функцию со всеми её 26-ю параметрами):

print longfunc( # перенос значений аргументов функций на несколько строк не имеет ничего общего с каррингом. Нет, правда.

   10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120,
   130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230,
   240, 250, 260

) # да, опять выведет 2387403. </source>

JavaScript

В JavaScript областью видимости локальных переменных (объявляемых словом var) является тело функции, внутри которой они определены.[1]

Если вы объявляете функцию внутри другой функции, первая получает доступ к переменным и аргументам последней: <source lang="javascript"> function outerFn(myArg) {

  var myVar;
  function innerFn() {
     //имеет доступ к myVar и myArg
  }

} </source>

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

Рассмотрим пример — функцию, возвращающую количество собственных вызовов:

<source lang="javascript"> function createCounter() {

  var numberOfCalls = 0;
  return function() {
     return ++numberOfCalls;
  }

} var fn = createCounter(); fn(); //1 fn(); //2 fn(); //3 </source>

Perl

Пример с использованием замыканий на Perl:

<source lang="perl">

  1. возвращает анонимную функцию

sub adder{ my $x = shift; # в котором x - свободная переменная, sub{ my $y = shift; # а y - связанная переменная $x + $y; }; }

$add1 = adder(1); # делаем процедуру для прибавления 1 print $add1->(10); # печатает 11

$sub1 = adder(-1); # делаем процедуру для вычитания 1 print $sub1->(10); # печатает 9 </source>

Lua

Пример с использованием замыканий на Lua: <source lang="lua"> function makeaddfunc(x)

 -- Возвращает новую анонимную функцию, которая добавляет x к аргументу
 return function(y)
   -- Когда мы ссылаемся на переменную x, которая вне текущей области,
   -- и время жизни которой меньше, чем этой анонимной функции, 
   -- Lua создаёт замыкание.
   return x + y
 end

end plustwo = makeaddfunc(2) print(plustwo(5)) -- Выводит 7 </source>

См. также

Примечания

  1. Владимир Агафонкин Замыкания в JavaScript


bg:Затваряне (информатика) de:Closure en:Closure (computer science) es:Clausura (informática) fr:Fermeture (informatique) it:Chiusura (informatica) ja:クロージャ nl:Closure pl:Domknięcie (programowanie) pt:Closure simple:Closure (computer science) th:ส่วนปิดคลุม (วิทยาการคอมพิวเตอร์) uk:Замикання (програмування) zh:闭包 (计算机科学)

Если вам нравится SbUP.com Сайт, вы можете поддержать его - BTC: bc1qppjcl3c2cyjazy6lepmrv3fh6ke9mxs7zpfky0 , TRC20 и ещё....