Quelle est la différence entre une "fermeture" et un "lambda'?

Quelqu'un pourrait-il expliquer? Je comprends les concepts de base qui les sous-tendent, mais je les vois souvent utilisés de façon interchangeable et je m'y perds.

Et maintenant que nous sommes ici, en quoi diffèrent-ils une fonction régulière?

691
demandé sur S.L. Barth 2008-10-21 07:12:54

11 réponses

Un lambda est juste une fonction anonyme - une fonction définie sans nom. Dans certaines langues, telles que Scheme, elles sont équivalentes à des fonctions nommées. En fait, la définition de la fonction est réécrite comme liant une lambda à une variable interne. Dans d'autres langues, comme Python, il y a quelques distinctions (plutôt inutiles) entre eux, mais ils se comportent de la même façon autrement.

Un fermeture est tout fonction se ferme sur le environnement dans lequel il a été défini. Cela signifie qu'il peut accéder à des variables qui ne sont pas dans sa liste de paramètres. Exemples:

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

cela causera une erreur, parce que func ne ne se ferme pas l'environnement dans anotherfunc - h n'est pas défini. func ne se ferme que sur l'environnement mondial. Cela fonctionnera:

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

parce que ici, func est défini dans anotherfunc , et en python 2.3 et plus (ou un certain nombre comme celui-ci) quand ils presque obtenu fermetures correctes (mutation ne fonctionne toujours pas), cela signifie qu'il se ferme sur anotherfunc de l 'environnement et peut accéder aux variables à l'intérieur de celui-ci. En Python 3.1+, mutation fonctionne aussi quand on utilise le nonlocal mot-clé .

Un autre point important - func continuera à se fermer au-dessus de anotherfunc l 'environnement même si elle n'est plus évaluée dans anotherfunc . Ce code fonctionnera aussi:

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

print anotherfunc(10)()

ceci affichera 10.

cela, comme vous le remarquez, n'a rien à voir avec lambda s - ils sont deux concepts différents (bien que liés).

617
répondu Claudiu 2016-08-19 23:00:46

quand la plupart des gens pensent à fonctions , ils pensent à fonctions nommées :

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

ceux-ci sont appelés par leur nom, bien sûr:

foo(); //returns the string above

avec lambda expressions , vous pouvez avoir fonctions anonymes :

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

avec l'exemple ci-dessus, vous pouvez appeler la lambda à travers la variable it was attribué à:

foo();

plus utile que d'attribuer des fonctions anonymes à des variables, cependant, les passer à ou de fonctions d'ordre supérieur, c.-à-d., les fonctions qui acceptent/renvoient d'autres fonctions. Dans beaucoup de ces cas, il n'est pas nécessaire de nommer une fonction:

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)}); 

Un fermeture peut être un nom ou la fonction anonyme, mais il est connu en tant que tel lorsqu'il "se ferme sur" les variables dans le champ d'application où la fonction est défini, c'est à dire, la fermeture de toujours se référer à l'environnement avec l'variables qui sont utilisées dans la fermeture elle-même. Voici une fermeture nommée:

@x = 0;

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

incrementX(); // x now equals 1

Qui ne semble pas beaucoup, mais si c'était dans une autre fonction et que vous avez passé incrementX à une fonction externe?

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)

C'est comme ça qu'on obtient des objets stateful dans la programmation fonctionnelle. Puisque nommer "incrementX" n'est pas nécessaire, vous pouvez utiliser un lambda dans ce case:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }
166
répondu Mark Cidade 2008-10-21 03:59:32

il y a beaucoup de confusion autour de lambdas et des fermetures, même dans les réponses à cette question sur les empilements ici. Au lieu de demander aux programmeurs aléatoires qui ont appris des fermetures de la pratique avec certains langages de programmation ou d'autres programmeurs ignorants, prenez un voyage à la source (où tout a commencé). Et puisque les lambdas et les fermetures proviennent de Lambda Calculus inventé par L'Église Alonzo dans les années 30 avant première les ordinateurs électroniques ont même existé, c'est la source je parle.

Lambda Calculus est le langage de programmation le plus simple au monde. Les seules choses que vous pouvez faire:►

  • de l'APPLICATION: l'Application d'une expression à l'autre, notée f x .

    (pensez-y comme un appel de fonction , où f est la fonction et x est son seul paramètre)
  • ABSTRACTION: lie un symbole existant dans une expression pour marquer que ce symbole est juste une "fente", une boîte vide attendant d'être remplie de valeur, une "variable" comme elle étaient. Elle se fait en préparant une lettre grecque λ (lambda), puis le nom symbolique (par exemple x ), puis un point . avant l'expression. Ceci convertit ensuite l'expression en une fonction 1519480920 paramètre "expecting one .

    par exemple: λx.x+2 prend l'expression x+2 et indique que le symbole x dans cette expression est une variable liée – il peut être substitué avec une valeur que vous fournissez comme paramètre.

    Notez que la fonction définie de cette façon est anonyme – il n'a pas de nom, donc vous ne pouvez pas vous y référer encore, mais vous pouvez appeler immédiatement (rappelez-vous la demande?) en lui fournissant le paramètre qu'il attend, comme ceci: (λx.x+2) 7 . Puis l'expression (dans ce cas une valeur littérale) 7 est remplacée par x dans la sous-expression x+2 de la lambda appliquée, donc vous obtenez 7+2 , qui se réduit ensuite à 9 par des règles arithmétiques communes.

donc nous avons résolu l'un des mystères:

lambda est la fonction anonyme de l'exemple ci-dessus, λx.x+2 .


Dans différents langages de programmation, la syntaxe de l'abstraction fonctionnelle (lambda) peut différer. Par exemple, dans JavaScript il ressemble à ceci:
function(x) { return x+2; }

et vous pouvez l'appliquer immédiatement à un paramètre comme celui-ci:

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

ou vous pouvez stocker cette fonction anonyme (lambda) dans une variable:

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

qui lui donne effectivement un nom f , vous permettant de vous y référer et de l'appeler plusieurs fois plus tard, par exemple:

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

mais vous n'aviez pas à le nommer. Vous pouvez l'appeler immédiatement:

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

à LISP, les lambdas sont fabriqués comme ceci:

(lambda (x) (+ x 2))

et vous pouvez appeler une telle lambda en l'appliquant immédiatement à un paramètre:

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


OK, maintenant il est temps de résoudre l'autre mystère: qu'est ce qu'un fermeture . Pour ce faire, parlons de symboles ( variables ) dans les expressions lambda.

comme je l'ai dit, ce que l'abstraction lambda fait est liant un symbole dans sa sous-expression, de sorte qu'il devient un substituable paramètre . Ce symbole est appelé lié . Mais que faire si il y a d'autres symboles dans l'expression? Par exemple: λx.x/y+2 . Dans cette expression, le symbole x est lié par l'abstraction lambda λx. qui le précède. Mais l'autre symbole, y , n'est pas lié – il est libre . Nous ne savons pas ce que c'est et d'où il vient, on ne sait pas de quoi il signifie et ce valeur il représente, et donc nous ne pouvons pas évaluer cette expression jusqu'à ce que nous trouvions ce que signifie y .

en fait, il en va de même pour les deux autres symboles, 2 et + . C'est juste que nous sommes tellement familiers avec ces deux symboles que nous oublions généralement que l'ordinateur ne les connaît pas et nous devons lui dire ce qu'ils signifient en les définissant quelque part, par exemple dans une bibliothèque ou la langue elle-même.

vous pouvez penser à la symboles libres tels que définis ailleurs, en dehors de l'expression, dans son" contexte environnant", qui est appelé son environnement . L'environnement pourrait être une plus grande expression que cette expression fait partie de (comme Qui-Gon Jinn a dit:" il y a toujours un plus gros poisson";)), ou dans une bibliothèque, ou dans la langue elle-même (comme un primitif ).

cela nous permet de diviser les expressions lambda en deux catégories:

  • expressions fermées: chaque symbole qui apparaît dans ces expressions est lié par une abstraction lambda. En d'autres termes, ils sont autonomes ; ils ne nécessitent pas d'évaluation du contexte environnant. Ils sont également appelés combinateurs .
  • expressions ouvertes: certains symboles dans ces expressions ne sont pas lié - qui est, certains des symboles qui s'y trouvent sont free et ils nécessitent certaines informations externes, et ils ne peuvent donc pas être évalués tant que vous n'avez pas fourni les définitions de ces symboles.

vous pouvez fermer un ouvert expression lambda en fournissant le environnement , qui définit tous ces symboles libres en les liant à certaines valeurs (qui peuvent être des nombres, des chaînes, des fonctions anonymes aka les lambdas, quoi que...).

et voici le fermeture partie:

Le fermeture d'une lambda expression est cet ensemble particulier de symboles définis dans le contexte extérieur (environnement) qui donnent des valeurs au symboles libres dans cette expression, les rendant non-libre plus. Il tourne une ouverte expression lambda, qui encore contient des symboles libres "non définis", dans un fermé , qui n'a plus de symboles libres.

par exemple, si vous avez l'expression lambda suivante: λx.x/y+2 , le symbole x est lié, tandis que le symbole y est libre, donc l'expression est open et ne peut pas être évaluée à moins que vous disiez ce que y signifie (Et la même chose avec + et 2 , qui sont également libres). Mais supposons que vous ayez aussi un environnement comme ceci:

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

Ce environnement fournit des définitions pour tous les "undefined" (gratuit) symboles de notre expression lambda ( y , + , 2 ), et de supplémentaires de plusieurs symboles ( q , w ). Les symboles que nous devons définir sont ce sous-ensemble de l'environnement:

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

et ce est précisément la fermeture de notre expression lambda: >

en d'autres termes, il ferme une expression lambda ouverte. C'est ici que le nom fermeture est venu en premier lieu, et c'est pourquoi tant de réponses de personnes dans ce fil ne sont pas tout à fait corrects :P


Alors, pourquoi sont-ils trompés? Pourquoi tant d'entre eux disent-ils que les fermetures sont des structures de données la mémoire, ou certaines caractéristiques des langues qu'ils utilisent, ou pourquoi confondent-ils les fermetures avec lambdas? :P

Eh bien, les marketoids d'entreprise de Sun/Oracle, Microsoft, Google etc. sont à blâmer, parce que c'est ce qu'ils appellent ces constructions dans leurs langues (Java, C#, Go etc.). Ils appellent souvent "fermetures" ce qui est censé être juste lambdas. Ou ils appellent "fermetures" une technique particulière qu'ils ont utilisée pour mettre en œuvre la portée lexicale, c'est-à-dire, le fait qu'une fonction peut accéder aux variables qui étaient définies dans son champ d'application externe au moment de sa définition. Ils disent souvent que la fonction "enferme" ces variables, c'est-à-dire les capture dans une certaine structure de données pour les sauver d'être détruits après la fonction extérieure termine l'exécution. Mais cela ne fait que créer post factum l'étymologie du folklore " et le marketing, ce qui ne fait que rendre les choses plus confuses, parce que chaque vendeur de langue utilise sa propre terminologie.

et c'est pair pire parce qu'il y a toujours un peu de vérité dans ce qu'ils disent, ce qui ne permet pas de le rejeter facilement comme faux :P laissez-moi vous expliquer:

si vous voulez implémenter un langage qui utilise lambdas comme citoyens de première classe, vous devez leur permettre d'utiliser des symboles définis dans leur contexte environnant (c'est-à-dire, d'utiliser des variables libres dans vos lambdas). Et ces symboles doivent être présents même lorsque la fonction environnante retourne. Le problème est que ces symboles sont liés à un certain stockage local de la fonction (généralement sur la pile d'appels), qui ne sera plus là lorsque la fonction retourne. Par conséquent, pour qu'un lambda fonctionne comme vous l'attendez, vous devez "capturer" d'une manière ou d'une autre toutes ces variables libres de son contexte extérieur et les sauvegarder pour plus tard, même lorsque le contexte extérieur sera disparu. C'est-à-dire que vous devez trouver le fermeture de votre lambda (toutes ces variables externes qu'il utilise) et de le stocker ailleurs (soit par faire une copie, ou préparer de l'espace pour eux à l'avance, ailleurs que sur la pile). La méthode que vous utilisez pour atteindre cet objectif est un "détail de l'implémentation" de votre langue. Ce qui est important ici est le fermeture , qui est l'ensemble de variables libres de la environnement de votre lambda qui doivent être sauvés quelque part.

il n'a pas fallu trop longtemps pour que les gens commencent à appeler le structure de données réelle qu'ils utilisent dans les implémentations de leur langue pour implémenter la fermeture comme la "fermeture" elle-même. La structure ressemble habituellement à quelque chose comme ceci:

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

et ces structures de données sont transmises comme paramètres à d'autres fonctions, retournées de fonctions, et stockées dans des variables, pour représenter lambdas, et leur permettant d'accéder à leur environnement d'enclos ainsi que le code machine à exécuter dans ce contexte. Mais c'est juste une façon (un des beaucoup d') mettre en œuvre fermeture", pas le fermeture elle-même.

comme je l'ai expliqué ci-dessus, la fermeture d'une expression lambda est le sous-ensemble de définitions dans son environnement qui donnent des valeurs aux variables libres contenues dans cette expression lambda, effectivement fermeture l'expression (transformer une expression ouverte lambda, qui ne peut pas encore être évaluée, en une expression fermé lambda expression, qui peut alors être évalué, puisque tous les symboles qu'il contient sont maintenant définis).

autre Chose c'est juste un "culte du cargo" et "la magie vaudou" de programmeurs et de la langue vendeurs ignorent les véritables racines de ces notions.

j'espère que cela répondra à vos questions. Mais si vous aviez des questions, n'hésitez pas à les poser dans les commentaires, et je vais essayer d'expliquer mieux.

156
répondu SasQ 2018-06-16 18:54:27

toutes les fermetures ne sont pas des lambdas et toutes les lambdas ne sont pas des fermetures. Les deux sont des fonctions, mais pas nécessairement de la manière dont nous sommes habitués à les connaître.

un lambda est essentiellement une fonction qui est définie en ligne plutôt que la méthode standard de déclaration des fonctions. Les Lambdas peuvent souvent se transmettre sous forme d'objets.

une fermeture est une fonction qui entoure son état en faisant référence à des champs extérieurs à son corps. Le clos de l'état reste à travers les invocations de la fermeture.

dans un langage orienté objet, les fermetures sont normalement fournies par l'intermédiaire d'objets. Cependant, certains langages OO (par exemple C#) implémentent une fonctionnalité spéciale qui est plus proche de la définition des fermetures fournie par les langages purement fonctionnels (tels que lisp) qui n'ont pas d'objets pour l'état de fermeture.

ce qui est intéressant, c'est que L'introduction de Lambdas et de fermetures en C# apporte programmation fonctionnelle plus près à intégrer l'utilisation.

52
répondu Michael Brown 2014-02-23 23:14:44

c'est aussi simple que cela: lambda est une construction de langage, c'est-à-dire une syntaxe simple pour les fonctions anonymes; une fermeture est une technique pour la mettre en œuvre -- ou n'importe quelles fonctions de première classe, pour cette matière, nommé ou anonyme.

plus précisément, une fermeture est la façon dont une fonction de première classe est représentée à l'exécution, comme une paire de son" code "et un environnement" fermeture " sur toutes les variables non locales utilisées dans ce code. De cette façon, ces variables sont encore accessibles même lorsque les portées extérieures d'où ils proviennent ont déjà été enlevées.

malheureusement, il existe de nombreux langages qui ne prennent pas en charge les fonctions en tant que valeurs de première classe, ou ne les prennent en charge que sous une forme invalidée. Ainsi, les gens utilisent souvent le terme "fermeture" pour distinguer "la chose réelle".

14
répondu Andreas Rossberg 2014-07-31 07:47:37

du point de vue des langages de programmation, ce sont deux choses complètement différentes.

fondamentalement, pour un langage complet, nous n'avons besoin que d'éléments très limités, par exemple l'abstraction, l'application et la réduction. L'Abstraction et l'application fournissent la façon dont vous pouvez construire l'expression lamdba, et la réduction dertermines le sens de l'expression lambda.

Lambda fournit un moyen vous pouvez abstraire le processus de calcul. pour exemple, pour calculer la somme de deux nombres, un processus qui prend deux paramètres x, y et retourne x+y peut être abstrait. Dans scheme, vous pouvez l'écrire comme

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

, Vous pouvez renommer les paramètres, mais la tâche qu'il accomplit ne change pas. Dans presque tous les langages de programmation, vous pouvez donner l'expression lambda un nom, qui sont des fonctions nommées. Mais il n'y a pas beaucoup de différence, ils peuvent être considérés conceptuellement comme du simple sucre de syntaxe.

OK, maintenant imaginez comment cela peut être mis en œuvre. Chaque fois que nous appliquons l'expression lambda à certaines expressions, par exemple

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

nous pouvons simplement remplacer les paramètres par l'expression à évaluer. Ce modèle est déjà très puissant. Mais ce modèle ne nous permet pas de changer les valeurs des symboles, par exemple nous ne pouvons pas imiter le changement de statut. Donc nous avons besoin d'un modèle plus complexe. Pour faire court, chaque fois que nous voulons calculer le sens de la lambda expression, nous mettons la paire de symbole et la valeur correspondante dans un environnement(ou une table). Ensuite, le reste (+ x y) est évalué en regardant les symboles correspondants dans le tableau. Maintenant, si nous fournir certaines primitives pour fonctionner sur l'environnement directement, nous pouvons modéliser les changements de statut!

avec ce fond, Vérifiez cette fonction:

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

Nous savons que lorsque nous évaluons l'expression lambda, x y sera lié dans une nouvelle table. Mais comment et où peut-on chercher z up? En fait z est appelé une variable libre. Il doit y avoir un extérieur un environnement qui contient z. Dans le cas contraire, le sens de l'expression ne peut pas être déterminé uniquement par X et Y. Pour que ce soit clair, vous pouvez écrire quelque chose comme suit dans scheme:

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

donc z serait lié à 1 dans une table extérieure. Nous obtenons toujours une fonction qui accepte deux paramètres, mais le vrai sens il dépend aussi de l'environnement extérieur. En d'autres termes l'environnement extérieur se ferme sur les variables libres. Avec l'aide de set! on peut créer une fonction dynamique, j'.e, ce n'est pas une fonction au sens mathématique. Ce qu'il renvoie dépend non seulement de l'entrée, mais aussi de z.

C'est quelque chose que vous connaissez déjà très bien, une méthode d'objets presque toujours s'appuie sur l'état des objets. C'est pourquoi certains disent: "les fermetures sont des objets du pauvre. "Mais nous pourrions aussi considérer les objets comme des fermetures de pauvres puisque nous comme la première classe de fonctions.

j'utilise le schéma pour illustrer les idées en raison de ce schéma est l'une des langues les plus anciennes qui a de véritables fermetures. Tous les matériaux ici sont beaucoup mieux présentés dans le chapitre 3 du SICP.

pour résumer, lambda et closure sont des concepts vraiment différents. Une lambda est une fonction. Une fermeture est une paire de lambda et l'environnement correspondant qui ferme la lambda.

12
répondu Wei Qiu 2014-03-19 00:11:15

Concept est le même que décrit ci-dessus, mais si vous êtes de PHP background, cela expliquer plus en utilisant le code PHP.

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

fonction ($v) { return $v > 2; } est la définition de la fonction lambda. On peut même stocker dans une variable, de sorte qu'il peut être réutilisable:

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

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

maintenant, que faire si vous voulez changer le nombre maximum autorisé dans le tableau filtré? Vous devriez écrire une autre fonction lambda ou créer une fermeture (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));

Une fermeture est une fonction qui est évaluée dans son propre environnement, qui a une ou plusieurs variables liées qui peuvent être accessibles lorsque la fonction est appelée. Ils viennent du monde de la programmation fonctionnelle, où il y a un certain nombre de concepts en jeu. Les fermetures sont comme les fonctions lambda, mais plus intelligentes dans le sens où elles ont la capacité d'interagir avec des variables de l'environnement extérieur où la fermeture est définie.

Voici un exemple plus simple de fermeture PHP:

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

$closure();

bien expliqué dans cet article.

7
répondu Developer 2014-07-31 07:41:33

cette question Est vieille et a obtenu de nombreuses réponses. Maintenant, avec Java 8 et Lambda officiel qui sont des projets de fermeture non officiels, il ravive la question.

la réponse dans le contexte Java (via Lambdas et les fermetures-Quelle est la différence? ):

"Une fermeture est une expression lambda jumelé avec un environnement qui lie chacun de ses variables libres d'une valeur. En Java, les expressions lambda seront implémentées par des moyens les deux termes sont donc devenus interchangeables dans la communauté."

6
répondu philippe lhardy 2016-02-22 21:54:43

pour parler simplement, la fermeture est un truc à propos de scope, lambda est une fonction anonyme. Nous pouvons réaliser la fermeture avec lambda plus élégamment et lambda est souvent utilisé comme un paramètre passé à une fonction plus élevée

4
répondu feilengcui008 2015-05-16 07:03:23

Un lambda est juste une fonction anonyme - une fonction définie sans nom.Une fermeture est toute fonction qui se ferme sur l'environnement dans lequel elle a été définie. Cela signifie qu'il peut accéder à des variables qui ne sont pas dans sa liste de paramètres.

0
répondu kumar 2017-07-26 13:28:39

cela dépend si une fonction utilise une variable externe ou non pour effectuer l'opération.

variables externes - variables définies en dehors du champ d'une fonction.

  • les expressions Lambda sont apatrides parce qu'elles dépendent de paramètres, de variables internes ou de constantes pour effectuer des opérations.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • Fermeture hold state parce qu'il utilise des variables externes (c'est-à-dire des variables définies en dehors de la portée du corps de fonction) ainsi que des paramètres et des constantes pour effectuer des opérations.

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

lorsque Java crée la fermeture, il conserve la variable n avec la fonction de sorte qu'elle puisse être référencée lorsqu'elle est passée à d'autres fonctions ou utilisée n'importe où.

0
répondu DIPANSHU GOYAL 2018-09-24 16:07:45