В чем разница между «закрытием» и «лямбдой»?

812

Может кто-нибудь объяснить? Я понимаю основные концепции, стоящие за ними, но часто вижу, что они используются взаимозаменяемо, и я запутываюсь.

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

sker
источник
85
Лямбды - это языковая конструкция (анонимные функции), замыкания - это метод реализации для реализации первоклассных функций (анонимных или нет). К сожалению, это часто путают многие люди.
Андреас Россберг
Для закрытия PHP, см. Php.net/manual/en/class.closure.php . Это не то, чего ожидает программист JavaScript.
PaulH
Ответ SasQ превосходен. ИМХО, этот вопрос был бы более полезным для пользователей SO, если бы он направлял зрителей к этому ответу.
AmigoNico

Ответы:

705

Лямбда просто анонимная функция - функция , определенная без имени. В некоторых языках, таких как Scheme, они эквивалентны именованным функциям. Фактически, определение функции переписывается как внутренняя привязка лямбды к переменной. В других языках, таких как Python, между ними есть некоторые (довольно ненужные) различия, но в остальном они ведут себя так же.

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

def func(): return h
def anotherfunc(h):
   return func()

Это приведет к ошибке, потому funcчто не закрывается в среде anotherfunc- hне определено. funcтолько закрывается над глобальной средой. Это будет работать:

def anotherfunc(h):
    def func(): return h
    return func()

Поскольку здесь funcопределено в anotherfuncи в Python 2.3 и более поздних версиях (или некотором подобном числе), когда они почти получили правильные замыкания (мутация по-прежнему не работает), это означает, что оно закрывается над anotherfunc средой и может обращаться к переменным внутри Это. В Python 3.1+, мутация тоже работает при использовании на nonlocalключевое слово .

Еще один важный момент - funcбудет продолжать закрывать над anotherfuncсредой, даже если она больше не оценивается в anotherfunc. Этот код также будет работать:

def anotherfunc(h):
    def func(): return h
    return func

print anotherfunc(10)()

Это напечатает 10.

Это, как вы заметили, не имеет ничего общего с лямбдами - это два разных (хотя и связанных) понятия.

Клаудиу
источник
1
Клаудиу, насколько я знаю, питон никогда не получал правильных закрытий. Они исправили проблему изменчивости, пока я не смотрел? Вполне возможно ...
Саймон
Саймон - ты прав, они все еще не совсем правы. я думаю, что Python 3.0 добавит хак, чтобы обойти это. (что-то вроде «нелокального» идентификатора). Я изменю свой ответ, чтобы отразить это.
Клавдиу
2
@AlexanderOrlov: Это и лямбды, и затворы. В Java раньше были замыкания через анонимные внутренние классы. Теперь эта функциональность стала проще синтаксически с помощью лямбда-выражений. Так что, вероятно, наиболее важным аспектом новой функции является то, что теперь есть лямбды. Это не правильно называть их лямбдами, они действительно лямбды. Почему авторы Java 8 могут не выделять тот факт, что они являются замыканиями, я не знаю.
Клавдиу
2
@AlexanderOrlov, поскольку лямбды Java 8 не являются истинными замыканиями, они являются имитациями замыканий. Они больше похожи на замыкания в Python 2.3 (нет изменчивости, поэтому требуется, чтобы переменные, на которые делается ссылка, были «эффективно финальными»), и внутренне компилируются в незамкнутые функции, которые принимают все переменные, на которые ссылается область действия, как скрытые параметры.
Логан Пикап
7
@Claudiu Я думаю, что ссылка на конкретную языковую реализацию (Python) может усложнить ответ. Вопрос полностью не зависит от языка (а также не имеет языковых тегов).
Матфея
319

Существует много путаницы вокруг лямбд и закрытий, даже в ответах на этот вопрос StackOverflow здесь. Вместо того, чтобы расспрашивать случайных программистов, которые узнали о замыканиях на практике с определенными языками программирования или другими невежественными программистами, отправляйтесь в путь к источнику (где все это началось). А так как лямбды и закрытие происходят из Lambda Исчисление изобретен Алонзо Черч еще в 30 - е годы , прежде чем первые электронные вычислительные машины существовали даже, это источник я говорю.

Лямбда-исчисление является самым простым языком программирования в мире. Единственное, что в нем можно сделать: ►

  • ПРИМЕНЕНИЕ: Применение одного выражения к другому, обозначается f x.
    (Думайте об этом как о вызове функции , где fфункция и xее единственный параметр)
  • АННОТАЦИЯ: связывает символ, встречающийся в выражении, чтобы отметить, что этот символ - просто «слот», пустое поле, ожидающее заполнения значением, как бы «переменная». Это делается путем добавления греческой буквы λ(лямбда), затем символического имени (например x), затем точки .перед выражением. Затем он преобразует выражение в функцию, ожидающую один параметр .
    Например: λx.x+2берет выражение x+2и сообщает, что символ xв этом выражении является связанной переменной - его можно заменить значением, которое вы указали в качестве параметра.
    Обратите внимание, что определенная таким образом функция является анонимной- он не имеет имени, так что вы не можете ссылаться на него, но вы можете немедленно вызвать его (помните приложение?), Снабжая его параметр он ждет, как это: (λx.x+2) 7. Затем выражение (в данном случае буквальное значение) 7подставляется, как xв подвыражении x+2применяемой лямбды, так что вы получите 7+2, что затем сводится к 9общим правилам арифметики.

Итак, мы решили одну из загадок:
лямбда - это анонимная функция из приведенного выше примера λx.x+2.


В разных языках программирования синтаксис функциональной абстракции (лямбда) может отличаться. Например, в JavaScript это выглядит так:

function(x) { return x+2; }

и вы можете сразу применить его к какому-либо параметру:

(function(x) { return x+2; })(7)

или вы можете сохранить эту анонимную функцию (лямбда) в некоторой переменной:

var f = function(x) { return x+2; }

который фактически дает ему имя f, позволяя вам ссылаться на него и вызывать его несколько раз позже, например:

alert(  f(7) + f(10)  );   // should print 21 in the message box

Но вы не должны были назвать это. Вы можете позвонить сразу:

alert(  function(x) { return x+2; } (7)  );  // should print 9 in the message box

В LISP лямбды сделаны так:

(lambda (x) (+ x 2))

и вы можете вызвать такую ​​лямбду, применив ее немедленно к параметру:

(  (lambda (x) (+ x 2))  7  )


Хорошо, теперь пришло время разгадать другую загадку: что такое закрытие . Чтобы сделать это, давайте поговорим о символах ( переменных ) в лямбда-выражениях.

Как я уже сказал, лямбда-абстракция выполняет привязку символа в его подвыражении, чтобы он стал заменяемым параметром . Такой символ называется связанным . Но что, если в выражении есть другие символы? Например: λx.x/y+2. В этом выражении символ xсвязан лямбда-абстракцией, λx.предшествующей ему. Но другой символ yне связан - он свободен . Мы не знаем, что это и откуда это происходит, поэтому мы не знаем, что это значит и какую ценность оно представляет, и поэтому мы не можем оценить это выражение, пока не выясним, что это yзначит.

На самом деле, то же самое относится и к двум другим символам, 2и +. Просто мы настолько знакомы с этими двумя символами, что обычно забываем, что компьютер их не знает, и нам нужно сказать, что они означают, определив их где-то, например, в библиотеке или в самом языке.

Вы можете думать о свободных символах как определенных где-то еще, вне выражения, в его «окружающем контексте», который называется его окружением . Окружение может быть большим выражением, частью которого является это выражение (как сказал Куай-Гон Джинн: «всегда есть большая рыба»;)), или в какой-то библиотеке, или в самом языке (как примитив ).

Это позволяет нам разделить лямбда-выражения на две категории:

  • ЗАКРЫТЫЕ выражения: каждый символ, встречающийся в этих выражениях, ограничен какой-то лямбда-абстракцией. Другими словами, они автономны ; они не требуют какого-либо окружающего контекста для оценки. Их также называют комбинаторами .
  • Выражения OPEN: некоторые символы в этих выражениях не являются связанными, то есть некоторые символы, встречающиеся в них, являются свободными и требуют некоторой внешней информации, и поэтому они не могут быть оценены, пока вы не предоставите определения этих символов.

Вы можете ЗАКРЫТЬ открытое лямбда-выражение, предоставив среду , которая определяет все эти свободные символы, связывая их с некоторыми значениями (которые могут быть числами, строками, анонимными функциями, т. Е. Лямбдами, что угодно…).

И вот идет закрытие часть: замыкание из лямбда - выражений это определенный набор символов , определенных во внешнем контексте (среда) , которые дают значения для свободных символов в этом выражении, что делает их несвободными больше. Он превращает открытое лямбда-выражение, которое все еще содержит «неопределенные» свободные символы, в закрытое , в котором больше нет свободных символов.

Например, если у вас есть следующее лямбда-выражение: λx.x/y+2символ xсвязан, а символ yсвободен, поэтому выражение можно openи не может быть оценено, если вы не скажете, что yозначает (и то же самое с +и 2, которые также свободны). Но предположим, что у вас также есть такая среда :

{  y: 3,
+: [built-in addition],
2: [built-in number],
q: 42,
w: 5  }

Эта среда поставляет определения для всех «не определено» (свободных) символов из нашего лямбда - выражения ( y, +, 2), а также несколько дополнительных символов ( q, w). Символы, которые нам нужно определить, являются этим подмножеством среды:

{  y: 3,
+: [built-in addition],
2: [built-in number]  }

и это как раз закрытие нашего лямбда-выражения:>

Другими словами, он закрывает открытое лямбда-выражение. Вот откуда и произошло закрытие имени, и поэтому ответы многих людей в этой теме не совсем верны: P


Так почему они ошибаются? Почему многие из них говорят, что замыкания - это некоторые структуры данных в памяти или некоторые особенности языков, которые они используют, или почему они путают замыкания с лямбдами? :П

Что ж, виноваты корпоративные маркетоиды Sun / Oracle, Microsoft, Google и т. Д., Потому что так они называли эти конструкции на своих языках (Java, C #, Go и т. Д.). Они часто называют «замыканиями», которые должны быть просто лямбдами. Или они называют «замыкания» конкретным методом, который они использовали для реализации лексической области видимости, то есть тот факт, что функция может получить доступ к переменным, которые были определены во внешней области во время ее определения. Они часто говорят, что функция «заключает» эти переменные, то есть захватывает их в некоторую структуру данных, чтобы сохранить их от уничтожения после завершения выполнения внешней функции. Но это только придуманная постфактум "фольклорная этимология" и маркетинг, которая только делает вещи более запутанными,

И это еще хуже из-за того факта, что в их словах всегда есть доля правды, которая не позволяет вам легко отклонить это как ложное: P Позвольте мне объяснить:

Если вы хотите реализовать язык, который использует лямбды в качестве граждан первого класса, вам нужно разрешить им использовать символы, определенные в их окружающем контексте (то есть использовать свободные переменные в ваших лямбдах). И эти символы должны быть там, даже когда окружающая функция возвращается. Проблема в том, что эти символы привязаны к некоторому локальному хранилищу функции (обычно в стеке вызовов), которого больше не будет, когда функция вернется. Следовательно, чтобы лямбда работала так, как вы ожидаете, вам нужно каким-то образом «захватить» все эти свободные переменные из внешнего контекста и сохранить их на потом, даже когда внешний контекст исчезнет. То есть нужно найти замыканиевашей лямбды (все эти внешние переменные, которые он использует) и сохраните ее где-нибудь еще (либо сделав копию, либо подготовив пространство для них заранее, где-то еще, чем в стеке). Фактический метод, который вы используете для достижения этой цели, является «подробностью реализации» вашего языка. Здесь важно замыкание , представляющее собой набор свободных переменных из окружения вашей лямбды, которые нужно где-то сохранить.

Людям не потребовалось слишком много времени, чтобы начать называть фактическую структуру данных, которую они используют в реализациях своего языка, чтобы реализовать замыкание как само замыкание. Структура обычно выглядит примерно так:

Closure {
   [pointer to the lambda function's machine code],
   [pointer to the lambda function's environment]
}

и эти структуры данных передаются в качестве параметров другим функциям, возвращаются из функций и сохраняются в переменных для представления лямбда-выражений и предоставления им доступа к окружающей их среде, а также к машинному коду для выполнения в этом контексте. Но это просто способ (один из многих) реализовать замыкание, а не само замыкание.

Как я объяснил выше, закрытие лямбда-выражения - это подмножество определений в его среде, которые присваивают значения свободным переменным, содержащимся в этом лямбда-выражении, эффективно закрывая выражение (превращая открытое лямбда-выражение, которое еще не может быть оценено, в закрытая лямбда - выражение, которое затем может быть оценено, так как все символы , содержащиеся в нем теперь определены).

Все остальное - это просто «культ груза» и «магия воу-ду» программистов и поставщиков языков, не подозревающих о реальных корнях этих понятий.

Я надеюсь, что это отвечает на ваши вопросы. Но если у вас есть дополнительные вопросы, не стесняйтесь задавать их в комментариях, и я постараюсь объяснить их лучше.

SasQ
источник
50
Лучший ответ, объясняющий вещи в общих чертах, а не в зависимости от языка
Шишир Арора
39
Мне нравится такой подход при объяснении вещей. Начиная с самого начала, объясняя, как все работает, а затем, как были созданы текущие заблуждения. Этот ответ должен идти к вершине.
Sharky
1
@SasQ> закрытие лямбда-выражения - это подмножество определений в его среде, которые присваивают значения свободным переменным, содержащимся в этом лямбда-выражении. <Так что в вашем примере структуры данных это означает, что более уместно сказать «указатель на среда лямбда-функции "это закрытие?
Джефф М
3
Хотя лямбда-исчисление кажется мне машинным языком, я должен согласиться с тем, что это «найденный» язык в отличие от «сделанного» языка. И, таким образом, гораздо менее подвержен произвольным соглашениям и гораздо больше подходит для захвата основной структуры реальности. Мы можем найти детали в Linq, JavaScript, F # более доступными / доступными, но лямбда-исчисление доходит до сути вопроса без отвлечения.
StevePoling
1
Я благодарен вам за то, что вы повторили свою точку зрения несколько раз, с немного отличающейся формулировкой каждый раз. Это помогает укрепить концепцию. Я хотел бы, чтобы больше людей сделали это.
Джонклаулор
175

Когда большинство людей думают о функциях , они думают о названных функциях :

function foo() { return "This string is returned from the 'foo' function"; }

Они называются по имени, конечно:

foo(); //returns the string above

С лямбда-выражениями вы можете иметь анонимные функции :

 @foo = lambda() {return "This is returned from a function without a name";}

В приведенном выше примере вы можете вызвать лямбду через переменную, которой она была назначена:

foo();

Однако более полезным, чем присвоение анонимных функций переменным, является передача их или из функций более высокого порядка, т. Е. Функций, которые принимают / возвращают другие функции. Во многих из этих случаев именование функции не требуется:

function filter(list, predicate) 
 { @filteredList = [];
   for-each (@x in list) if (predicate(x)) filteredList.add(x);
   return filteredList;
 }

//filter for even numbers
filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

Замыкание может быть именованными или анонимные функции, но , как известно , как например , когда он «закрывает над» переменными в объеме , где определяются функция, т.е. замыкание будет по- прежнему относится к окружающей среде с любым внешними переменными, которые используются в закрытие себя. Вот названное закрытие:

@x = 0;

function incrementX() { x = x + 1;}

incrementX(); // x now equals 1

Это не так много, но что, если все это было в другой функции, и вы перешли incrementXк внешней функции?

function foo()
 { @x = 0;

   function incrementX() 
    { x = x + 1;
      return x;
    }

   return incrementX;
 }

@y = foo(); // y = closure of incrementX over foo.x
y(); //returns 1 (y.x == 0 + 1)
y(); //returns 2 (y.x == 1 + 1)

Вот как вы получаете объекты с состоянием в функциональном программировании. Поскольку именование «incrementX» не требуется, вы можете использовать лямбду в этом случае:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }
Марк Сидаде
источник
14
на каком языке ты здесь?
Клавдиу
7
Это в основном псевдокод. В нем есть какой-то язык и JavaScript, а также проектируемый мной язык, называемый "@" ("at"), названный в честь оператора объявления переменной.
Марк Сидаде
3
@MarkCidade, так где же этот язык @? Есть ли документация и нет загрузки?
Pacerier
5
Почему бы не взять Javascript и добавить ограничение объявления переменных с начальным знаком @? Это сэкономило бы немного времени :)
Немоден
5
@Pacerier: я начал внедрять язык: github.com/marxidad/At2015
Марк Сидаде,
54

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

Лямбда - это, по сути, функция, которая определяется внутри строки, а не стандартный метод объявления функций. Лямбды часто можно передавать как объекты.

Замыкание - это функция, которая заключает в себе окружающее ее состояние путем обращения к полям, внешним по отношению к его телу. Закрытое состояние остается через вызовы замыкания.

В объектно-ориентированном языке замыкания обычно предоставляются через объекты. Однако некоторые языки OO (например, C #) реализуют специальные функциональные возможности, которые ближе к определению замыканий, предоставляемых чисто функциональными языками (такими как lisp), которые не имеют объектов для включения состояния.

Интересно то, что введение Lambdas и Closures в C # приближает функциональное программирование к общепринятому использованию.

Майкл Браун
источник
Итак, можем ли мы сказать, что замыкания являются подмножеством лямбд, а лямбды - подмножеством функций?
скер
Замыкания являются подмножеством лямбд ... но лямбды более специфичны, чем обычные функции. Как я уже сказал, лямбды определены в строке. По сути, нет способа ссылаться на них, если они не переданы другой функции или не возвращены в качестве возвращаемого значения.
Майкл Браун
19
Лямбды и замыкания - это подмножество всех функций, но существует только пересечение между лямбдами и замыканиями, где непересекающаяся часть замыканий будет называться функциями, которые являются замыканиями, а непересекающиеся лямды являются самодостаточными функциями с полностью связанные переменные.
Марк Сидаде
На мой взгляд, лямбды - это более фундаментальные понятия, чем функции. Это действительно зависит от языка программирования.
Вэй Цю
15

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

Точнее говоря, замыкание - это то, как функция первого класса представляется во время выполнения в виде пары ее «кода» и окружения, «замыкающего» все нелокальные переменные, используемые в этом коде. Таким образом, эти переменные по-прежнему доступны, даже если внешние области, в которых они происходят, уже закрыты.

К сожалению, существует множество языков, которые не поддерживают функции как первоклассные значения или поддерживают их только в ограниченном виде. Поэтому люди часто используют термин «закрытие», чтобы отличить «настоящую вещь».

Андреас Россберг
источник
12

С точки зрения языков программирования, это совершенно разные вещи.

В основном для полного языка Тьюринга нам нужны только очень ограниченные элементы, например, абстракция, применение и редукция. Абстракция и приложение предоставляют способ построения выражения лямбда, а сокращение определяет значение выражения лямбда.

Лямбда предоставляет способ, которым вы можете абстрагировать вычислительный процесс. например, чтобы вычислить сумму двух чисел, процесс, который принимает два параметра x, y и возвращает x + y, может быть абстрагирован. В схеме вы можете написать это как

(lambda (x y) (+ x y))

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

Хорошо, теперь представьте, как это можно реализовать. Всякий раз, когда мы применяем лямбда-выражение к некоторым выражениям, например,

((lambda (x y) (+ x y)) 2 3)

Мы можем просто заменить параметры выражением для оценки. Эта модель уже очень мощная. Но эта модель не позволяет нам изменять значения символов, например, мы не можем имитировать изменение статуса. Таким образом, нам нужна более сложная модель. Короче говоря, всякий раз, когда мы хотим вычислить значение лямбда-выражения, мы помещаем пару символов и соответствующее значение в среду (или таблицу). Затем остаток (+ xy) оценивается путем поиска соответствующих символов в таблице. Теперь, если мы предоставим некоторые примитивы для непосредственной работы с окружающей средой, мы можем смоделировать изменения статуса!

С этим фоном, проверьте эту функцию:

(lambda (x y) (+ x y z))

Мы знаем, что когда мы вычисляем лямбда-выражение, xy будет связано в новой таблице. Но как и где мы можем посмотреть вверх? На самом деле z называется свободной переменной. Должна быть внешняя среда, которая содержит z. В противном случае значение выражения не может быть определено только связыванием x и y. Чтобы прояснить это, вы можете написать что-то в схеме следующим образом:

((lambda (z) (lambda (x y) (+ x y z))) 1)

Таким образом, z будет привязан к 1 во внешней таблице. Мы все еще получаем функцию, которая принимает два параметра, но реальное значение этого также зависит от внешней среды. Другими словами, внешняя среда замыкается на свободные переменные. С помощью set !, мы можем сделать функцию состоящей из состояний, то есть это не функция в смысле математики. То, что он возвращает, зависит не только от ввода, но и от z.

Это то, что вы уже хорошо знаете, метод объектов почти всегда зависит от состояния объектов. Вот почему некоторые люди говорят, что «замыкания - это объекты бедняков». Но мы также можем рассматривать объекты как замыкания бедняков, поскольку нам действительно нравятся функции первого класса.

Я использую схему, чтобы проиллюстрировать идеи, потому что эта схема - один из самых ранних языков, который имеет реальные замыкания. Все материалы здесь намного лучше представлены в главе 3 SICP.

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

Вэй Цю
источник
Таким образом, можно заменить все замыкания на вложенные лямбды, пока свободных переменных больше не будет? В этом случае я бы сказал, что затворы можно рассматривать как особый вид лямбд.
Триларион
8

Концепция та же, что и описанная выше, но если вы из PHP, это дополнительно объясняется с использованием PHP-кода.

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, function ($v) { return $v > 2; });

function ($ v) {return $ v> 2; } - это определение лямбда-функции. Мы можем даже сохранить его в переменной, чтобы его можно было использовать повторно:

$max = function ($v) { return $v > 2; };

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max);

А что если вы хотите изменить максимально допустимое число в фильтруемом массиве? Вы должны написать другую лямбда-функцию или создать замыкание (PHP 5.3):

$max_comp = function ($max) {
  return function ($v) use ($max) { return $v > $max; };
};

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max_comp(2));

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

Вот более простой пример закрытия PHP:

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

$closure();

Красиво объясняется в этой статье.

разработчик
источник
7

Этот вопрос старый и получил много ответов.
Теперь с Java 8 и Official Lambda, которые являются неофициальными проектами закрытия, это поднимает вопрос.

Ответ в контексте Java (через лямбды и замыкания - какая разница? ):

«Закрытие - это лямбда-выражение, соединенное со средой, которая связывает каждую из своих свободных переменных со значением. В Java лямбда-выражения будут реализованы с помощью замыканий, поэтому два термина стали взаимозаменяемыми в сообществе».

Филипп Ларди
источник
Как Lamdas реализованы закрытием в Java? Означает ли это, что выражение Lamdas преобразуется в анонимный класс старого стиля?
хакдзюцу
5

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

feilengcui008
источник
4

Лямбда-выражение - это просто анонимная функция. в простой Java, например, вы можете написать это так:

Function<Person, Job> mapPersonToJob = new Function<Person, Job>() {
    public Job apply(Person person) {
        Job job = new Job(person.getPersonId(), person.getJobDescription());
        return job;
    }
};

где класс Function просто встроен в код Java. Теперь вы можете позвонить mapPersonToJob.apply(person)куда-нибудь, чтобы использовать его. это только один пример. Вот лямбда, прежде чем был синтаксис для этого. Лямбда - короткий путь для этого.

Закрытие:

лямбда становится замыканием, когда она может получить доступ к переменным вне этой области. я думаю, вы можете сказать, что это волшебство, оно волшебным образом может обернуться вокруг среды, в которой оно было создано, и использовать переменные вне своей области видимости (внешняя область видимости. так что для ясности закрытие означает, что лямбда может получить доступ к своей ВНЕШНЕЙ ОБЛАСТИ).

в Kotlin лямбда всегда может получить доступ к своему закрытию (переменные, которые находятся в его внешней области видимости)

j2emanue
источник
0

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

Внешние переменные - переменные, определенные вне области действия функции.

  • Лямбда-выражения не сохраняют состояния, поскольку они зависят от параметров, внутренних переменных или констант для выполнения операций.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • Замыкания содержат состояние, потому что для выполнения операций используются внешние переменные (т. Е. Переменные, определенные вне области действия тела функции), а также параметры и константы.

    int n = 2
    
    Function<Integer,Integer> closure = t -> {
        return t * n 
    }
    

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

ДИПАНШУ ГОЯЛ
источник