Яка різниця між "закриттям" та "лямбда"?


811

Може хтось пояснить? Я розумію основні поняття, що стоять за ними, але часто бачу, як вони використовуються взаємозамінно, і я плутаюся.

А тепер, коли ми тут, як вони відрізняються від звичайної функції?


85
Лямбди - це мовна конструкція (анонімні функції), закриття - це техніка реалізації для реалізації функцій першого класу (анонімних чи ні). На жаль, це часто плутає багато людей.
Андреас Росберг



Про закриття PHP див . У розділі php.net/manual/en/class.closure.php . Це не те, чого очікував програміст JavaScript.
PaulH

Відповідь SasQ відмінна. IMHO це питання було б кориснішим для користувачів SO, якщо він керував глядачів на цю відповідь.
AmigoNico

Відповіді:


703

Лямбда просто анонімна функція - функція , певна без імені. У деяких мовах, таких як Схема, вони еквівалентні названим функціям. Фактично, визначення функції переписується як прив'язка лямбда до змінної внутрішньо. В інших мовах, як-от 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 додасть хак для вирішення цього питання. (щось на зразок нелокального ідентифікатора). я зміню свій ansewr, щоб це відобразити.
Клавдіу

2
@AlexanderOrlov: Вони є і лямбдами, і закриттями. Ява раніше закривався через анонімні внутрішні класи. Тепер ця функціональність стала синтаксично простішою через лямбда-вирази. Тож, мабуть, найбільш релевантним аспектом нової функції є те, що зараз існують лямбда. Назвати їх лямбдами невірно, вони справді лямбди. Чому автори Java 8 можуть вирішити не підкреслювати той факт, що вони закриваються - це не те, що я знаю.
Клавдіу

2
@AlexanderOrlov, оскільки лямбди Java 8 не є справжніми закриттями, вони є імітацією закриття. Вони більш схожі на закриття Python 2.3 (відсутність змін, отже, вимога до змінних, що посилаються, "ефективно остаточні"), і внутрішньо компілюються до функцій незамкнення, які приймають усі змінні, на які посилається в обкладинці, як приховані параметри.
Логан Пікап

7
@Claudiu Я думаю, що посилання на конкретну реалізацію мови (Python) може надто ускладнити відповідь. Питання є повністю мовно-агностичним (як і не має тегів, що відповідають мові).
Матвій

318

Існує багато плутанини навколо лямбдашів та закриттів, навіть у відповідях на це питання StackOverflow тут. Замість того, щоб запитувати випадкових програмістів, які дізналися про закриття на практиці з певними мовами програмування або іншими неосвіченими програмістами, вирушайте у дорогу до джерела (звідки все почалося). А так як лямбда і закриття відбуваються з Lambda Обчислення винайдений Алонзо Черч ще в 30 - ті роки , перш ніж перші електронні обчислювальні машини існували навіть, це джерело я говорю.

Calbulus Lambda - найпростіша мова програмування у світі. Єдине, що ви можете зробити в ньому: ►

  • ЗАСТОСУВАННЯ: Застосування одного виразу до іншого, позначеного 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і +. Просто ми настільки знайомі з цими двома символами, що зазвичай забуваємо, що комп'ютер їх не знає, і нам потрібно розповісти, що вони означають, де-небудь визначивши їх, наприклад, у бібліотеці чи самій мові.

Ви можете думати про вільні символи, визначені десь в іншому місці, поза виразом, у його "оточуючому контексті", який називається його середовищем . Навколишнє середовище може бути більшим вираженням того, що цей вираз є частиною (як сказав Куї-Гон Джінн: "Завжди є більша риба";)), або в якійсь бібліотеці, або в самій мові (як примітив ).

Це дозволяє нам розділити лямбда-вирази на дві категорії:

  • ЗАКРИТІ вирази: кожен символ, що зустрічається в цих виразах, пов'язаний деякою лямбда-абстракцією. Іншими словами, вони є самостійними ; вони не вимагають оцінки будь-якого оточуючого контексту. Їх ще називають комбінаторами .
  • Відкриті вирази: деякі символи в цих виразах не пов'язані - тобто деякі символи, що зустрічаються в них, є вільними, і вони вимагають деякої зовнішньої інформації, і тому вони не можуть бути оцінені, поки ви не надасте визначення цих символів.

Ви можете ЗАКРИТИ відкритий лямбда-вираз, надавши оточення , яке визначає всі ці вільні символи, прив’язуючи їх до якихось значень (це можуть бути числа, рядки, анонімні функції ака лямбда, що б не було ...).

І ось іде закриття частина: замикання з лямбда - виразів це певний набір символів , визначених в зовнішньому контексті (середа) , які дають значення для вільних символів в цьому виразі, що робить їх невільними більше. Він перетворює відкритий лямбда-вираз, який все ще містить деякі "невизначені" вільні символи, у закритий , у якому більше немає вільних символів.

Наприклад, якщо у вас є наступний лямбда-вираз:, λ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


То чому вони помиляються? Чому так багато з них кажуть, що закриття - це якась структура даних в пам'яті або деякі особливості мов, якими вони користуються, або чому вони плутають закриття з лямбдазами? : 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]
}

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

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

Все інше - це лише "культ вантажу" і "магія ву-ду" програмістів і мовних постачальників, які не знають про справжні корені цих понять.

Я сподіваюся, що відповість на ваші запитання. Але якщо у вас виникли якісь подальші запитання, сміливо запитайте їх у коментарях, і я спробую це пояснити краще.


50
Найкраща відповідь, що пояснює речі загалом, а не конкретні мови
Шишир Арора

39
Я люблю такий підхід при поясненні речей. Починаючи з самого початку, пояснюючи, як все працює, а потім, як створювались поточні помилки. Цю відповідь потрібно пройти до вершини.
Шаркі

1
@SasQ> Закриття лямбда-виразу - це підмножина визначень у його середовищі, які дають значення вільним змінним, що містяться в цьому лямбда-виразі Середовище лямбда-функції "є закриттям?
Джефф М

3
Хоча обчислення лямбда вважає мене машинною мовою, я повинен погодитися, що це "знайдена" мова на відміну від "зробленої" мови. Таким чином, значно менше підлягає довільним умовам, і набагато більше підходить для захоплення основної структури реальності. Ми можемо знайти специфіку в Linq, JavaScript, F # більш доступних / доступних, але обчислення Ламбди потрапляє до суті справи, не відволікаючись.
StevePoling

1
Я вдячний, що ви повторювали свою думку кілька разів, щоразу дещо відрізняючись формулюванням. Це допомагає посилити концепцію. Я б хотів, щоб це зробили більше людей.
johnklawlor

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
Це в основному псевдокод. У ньому є кілька lisp та JavaScript, а також мова, яку я розробляю, називається "@" ("at"), названа на честь оператора змінної декларації.
Марк Сідаде

3
@MarkCidade, тож де ця мова @? Чи є документація та завантаження?
Pacerier

5
Чому б не взяти Javascript і не додати обмеження в оголошенні змінних за допомогою знака @ @? Це трохи заощадить час :)
Немоден

5
@Pacerier: Я почав впроваджувати мову: github.com/marksidad/At2015
Марк Сідаде

54

Не всі закриття є лямбдами, і не всі лямбда є закриттями. Обидва є функціями, але не обов'язково таким чином, як ми звикли знати.

Лямбда - це, по суті, функція, яка визначається вбудованим, а не стандартним методом оголошення функцій. Лямбди часто можуть передаватися навколо як об’єкти.

Замикання - це функція, яка закриває його оточуючий стан шляхом посилання на поля, зовнішні до його тіла. Вкладений стан залишається через виклики закриття.

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

Цікавим є те, що впровадження лямбдасів і замикань в C # наближає функціональне програмування до використання основних засобів.


Отже, чи можна сказати, що закриття - це підмножина лямбд, а лямбда - це підмножина функцій?
sker

Замикання - це підмножина лямбд ... але лямбди більш особливі, ніж звичайні функції. Як я вже казав, лямбди визначаються вбудованими. По суті, немає способу посилання на них, якщо вони не передані іншій функції або повернуті як повернене значення.
Майкл Браун

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 називається вільною змінною. Повинно бути зовнішнє середовище, яке містить z. Інакше значення виразу не може бути визначене лише прив'язкою x і y. Щоб це було зрозуміло, ви можете написати щось наступне на схемі:

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

Таким чином, z буде прив’язаний до 1 у зовнішній таблиці. Ми все ще отримуємо функцію, яка приймає два параметри, але реальний сенс її також залежить від зовнішнього середовища. Іншими словами, зовнішнє середовище закривається на вільних змінних. За допомогою набору! Ми можемо зробити функцію стаціонарною, тобто це не функція в сенсі математики. Те, що він повертає, залежить не тільки від введення даних, але й z.

Це те, що ви вже дуже добре знаєте, метод об’єктів майже завжди покладається на стан об’єктів. Ось чому деякі кажуть, що "закриття - це об'єкти бідної людини". Але ми також можемо вважати об'єкти закриттями для бідних людей, оскільки нам дуже подобаються функції першого класу.

Я використовую схему, щоб проілюструвати ідеї завдяки цій схемі є однією з найдавніших мов, яка має справжні закриття. Усі матеріали тут набагато краще представлені у розділі 3 SICP.

Підводячи підсумок, лямбда і закриття - це дійсно різні поняття. Лямбда - це функція. Закриття - це пара лямбда та відповідне середовище, яке закриває лямбда.


Тож можна було замінити всі закриття вкладеними лямбдами, поки вільних змінних вже немає? У цьому випадку я б сказав, що закриття можна розглядати як особливий вид лямбда.
Триларіон

8

Концепція така ж, як описана вище, але якщо ви з фонового режиму PHP, це додатково пояснить за допомогою PHP-коду.

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

функція ($ 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 (через Lambdas та закриття - яка різниця? ):

"Закриття - це лямбда-вираз, поєднаний із середовищем, яке пов'язує кожну з його вільних змінних до значення. У Java вирази лямбда будуть реалізовані за допомогою закриття, тому два терміни стали взаємозамінно використовуватися у спільноті."


Як реалізуються Lamdas шляхом закриття на Java? Чи означає це, що вираз Lamdas перетворюється на анонімний клас старого стилю?
hackjutsu

5

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


4

Вираз лямбда - це лише анонімна функція. Наприклад, у звичайній яві ви можете написати це так:

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

де функція класу просто вбудована в код Java. Тепер ви можете mapPersonToJob.apply(person)кудись зателефонувати, щоб скористатися цим. ось лише один приклад. Це лямбда, перш ніж для цього був синтаксис. Для цього лямбда - скорочення.

Закриття:

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

у Котліні лямбда завжди може отримати доступ до її закриття (змінні, що знаходяться в її зовнішній області)


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 з функцією, щоб її можна було посилати при передачі іншим функціям або будь-де використовувати її.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.