web-dev-qa-db-fra.com

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

Quelqu'un pourrait-il expliquer? Je comprends les concepts de base derrière eux mais je les vois souvent utilisés de manière interchangeable et je me perds 

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

725
sker

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

UNE fermeture est une fonction qui ferme sur la environnement dans lequel il a été défini. Cela signifie qu'il peut accéder aux variables ne figurant pas dans sa liste de paramètres. Exemples:

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

Cela provoquera une erreur, car func ne fermer sur l'environnement dans anotherfunc - h n'est pas défini. func ne ferme que l'environnement global. Cela fonctionnera:

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

Parce qu'ici, func est défini dans anotherfunc, et dans python 2.3 et supérieur (ou un nombre comme celui-ci) quand ils presque les fermetures sont correctes (la mutation ne fonctionne toujours pas), cela signifie ferme sur anotherfunc's et peut accéder aux variables qu'il contient. En Python 3.1+, la mutation fonctionne également avec le mot clé nonlocal .

Un autre point important - func continuera à fermer sur l'environnement de anotherfunc même s'il n'est plus évalué dans anotherfunc. Ce code fonctionnera également:

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

print anotherfunc(10)()

Cela va imprimer 10.

Comme vous le remarquerez, cela n'a rien à voir avec lambdas - ce sont deux concepts différents (bien que liés).

642
Claudiu

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 expressions lambda, 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 le lambda via la variable à laquelle il a été affecté:

foo();

Cependant, il est plus utile d’attribuer des fonctions anonymes à des variables que de les transmettre à des fonctions d’ordre supérieur, c’est-à-dire des 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)}); 

Une fermeture peut être une fonction nommée ou anonyme, mais est connue en tant que telle quand elle "ferme" les variables dans l'étendue où la fonction est définie, c'est-à-dire que la fermeture fera toujours référence à l'environnement avec toutes les variables externes qui sont utilisés dans la fermeture elle-même. Voici une fermeture nommée:

@x = 0;

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

incrementX(); // x now equals 1

Cela ne semble pas beaucoup, mais que se passerait-il si tout cela était dans une autre fonction et que vous passiez 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)

Voici comment vous obtenez des objets avec état dans la programmation fonctionnelle. Comme il n'est pas nécessaire de nommer "incrementX", vous pouvez utiliser un lambda dans ce cas:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }
168
Mark Cidade

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 que nous avons l'habitude de connaître.

Un lambda est essentiellement une fonction définie en ligne plutôt que la méthode standard de déclaration de fonctions. Les lambda peuvent souvent être échangés en tant qu'objets.

Une fermeture est une fonction qui englobe son environnement en référençant des champs extérieurs à son corps. L'état inclus reste en place lors des invocations de la fermeture.

Dans un langage orienté objet, les fermetures sont normalement fournies à travers des objets. Cependant, certains OO langages (par exemple, C #) implémentent des fonctionnalités spéciales plus proches de la définition des fermetures fournies par des langages purement fonctionnels (tels que LISP) qui ne possèdent pas d’objets à clôturer.

Ce qui est intéressant, c’est que l’introduction de Lambdas et de Closures en C # rapproche la programmation fonctionnelle de l’usage courant.

52
Michael Brown

C’est aussi simple que cela: lambda est une construction de langage, c’est-à-dire une simple syntaxe pour les fonctions anonymes; une fermeture est une technique pour la mettre en œuvre - ou toute fonction de première classe, nommée ou anonyme.

Plus précisément, une fermeture est la façon dont une fonction first-class est représentée à l'exécution, sous la forme d'une paire de son "code" et d'un environnement "fermant" sur toutes les variables non locales utilisées dans ce code. De cette façon, ces variables sont toujours accessibles même lorsque les étendues externes où elles ont été créées ont déjà été sorties.

Malheureusement, de nombreuses langues ne prennent pas en charge les fonctions en tant que valeurs de premier ordre ou ne les prennent en charge que sous une forme invalidée. Les gens utilisent donc souvent le terme "fermeture" pour distinguer "la réalité".

14
Andreas Rossberg

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

Fondamentalement, pour un langage complet de Turing, nous n’avons besoin que d’éléments très limités, par exemple. abstraction, application et réduction. L'abstraction et l'application fournissent le moyen de construire l'expression de lamdba et la réduction détermine la signification de l'expression lambda.

Lambda fournit un moyen de résumer le processus de calcul. Par exemple, pour calculer la somme de deux nombres, un processus prenant deux paramètres x, y et renvoyant x + y peut être extrait. Dans le schéma, vous pouvez l'écrire comme

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

Vous pouvez renommer les paramètres, mais la tâche effectuée ne change pas. Dans presque tous les langages de programmation, vous pouvez attribuer un nom à l'expression lambda, nommée fonction. Mais il n'y a pas beaucoup de différence, ils peuvent être conceptuellement considérés comme un simple sucre syntaxique.

OK, imaginez maintenant comment cela peut être implémenté. Chaque fois que nous appliquons l'expression lambda à certaines expressions, par exemple.

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

Nous pouvons simplement substituer les paramètres avec 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. Nous avons donc besoin d’un modèle plus complexe. Pour faire court, chaque fois que nous voulons calculer la signification de l'expression lambda, nous plaçons la paire de symbole et la valeur correspondante dans un environnement (ou une table). Ensuite, le reste (+ x y) est évalué en recherchant les symboles correspondants dans le tableau . Maintenant, si nous fournissons des primitives pour agir directement sur l'environnement, nous pouvons modéliser les changements d'état!

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ù pouvons-nous chercher? En fait, z est appelé une variable libre. Il doit y avoir un environnement externe Qui contient z. Sinon, la signification de l'expression ne peut être déterminée en liant seulement x et y. Pour que cela soit clair, vous pouvez écrire quelque chose comme suit dans le schéma:

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

Donc, z serait lié à 1 dans une table externe. Nous avons toujours une fonction qui accepte deux paramètres mais dont la signification réelle dépend également de l’environnement externe ..__ En d’autres termes, l’environnement externe se ferme sur les variables libres. Avec l'aide de set !, nous pouvons rendre la fonction stateful, c'est-à-dire que ce n'est pas une fonction au sens des mathématiques. Ce qu'il retourne dépend non seulement de l'entrée, mais également de z.

C’est quelque chose que vous connaissez déjà très bien, une méthode d’objets repose presque toujours sur l’état des objets. C'est pourquoi certaines personnes disent que "les fermetures sont des objets du pauvre homme", mais nous pourrions aussi considérer les objets comme des fermetures du pauvre, car nous aimons vraiment les fonctions de première classe.

J'utilise schéma pour illustrer les idées car ce schéma est l’un des premiers langages ayant des fermetures réelles. Tous les documents présentés ici sont bien mieux présentés dans le chapitre 3 du SICP.

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

12
Wei Qiu

Le concept est identique à celui décrit ci-dessus, mais si vous êtes issu de l'arrière-plan PHP, cette explication supplémentaire à l'aide de 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. Nous pouvons même le stocker dans une variable, afin qu'il soit réutilisable:

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

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

Maintenant, que se passe-t-il si vous souhaitez modifier le nombre maximal 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 comporte une ou plusieurs variables liées auxquelles il est possible d'accéder lorsque la fonction est appelée. Ils viennent du monde de la programmation fonctionnelle, où plusieurs concepts sont en jeu. Les fermetures sont comme des fonctions lambda, mais plus intelligentes dans le sens où elles ont la capacité d’interagir avec des variables de l’environnement extérieur du lieu où la fermeture est définie.

Voici un exemple plus simple de fermeture de PHP:

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

$closure();

Bien expliqué dans cet article.

7
Developer

Cette question est ancienne et a beaucoup de réponses. Désormais, avec Java 8 et Official Lambda, projets de clôture non officiels, la question se pose à nouveau.

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

"Une fermeture est une expression lambda associée à un environnement qui lie chacune de ses variables libres à une valeur. En Java, les expressions lambda sont implémentées au moyen de fermetures. Les deux termes sont donc utilisés de manière interchangeable dans la communauté."

6
philippe lhardy

En termes simples, la fermeture est une astuce à propos de la portée, lambda est une fonction anonyme. Nous pouvons réaliser la fermeture avec lambda de façon plus élégante et lambda est souvent utilisé comme paramètre transmis à une fonction supérieure.

4
feilengcui008

Une expression Lambda est juste une fonction anonyme. en Java, par exemple, vous pouvez l'écrire comme ceci:

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

où la classe Function est simplement construite en code Java. Maintenant, vous pouvez appeler mapPersonToJob.apply(person) quelque part pour l'utiliser. ce n'est qu'un exemple. C'est un lambda avant qu'il y ait une syntaxe pour cela. Lambdas un raccourci pour cela.

Fermeture:

un Lambda devient une fermeture lorsqu'il peut accéder aux variables en dehors de cette portée. Je suppose que vous pouvez dire sa magie, il peut, comme par magie, englober l'environnement dans lequel il a été créé et utiliser les variables hors de son champ d'application (champ externe. Donc, pour être clair, une fermeture signifie qu'un lambda peut accéder à son OUTER SCOPE.

dans Kotlin, un lambda peut toujours accéder à sa fermeture (les variables qui se trouvent dans son périmètre externe)

1
j2emanue

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 cadre d'une fonction.

  • Les expressions Lambda sont stateless car Cela dépend des paramètres, des variables internes ou des constantes pour effectuer les opérations.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • Closures hold state car il utilise des variables externes (c'est-à-dire définies en dehors de la portée du corps de la 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 une fermeture, il conserve la variable n avec la fonction afin qu'elle puisse être référencée lorsqu'elle est transmise à d'autres fonctions ou utilisée n'importe où.

0
DIPANSHU GOYAL