J’ai posé une question sur Currying et fermetures ont été mentionnés. Ce qui est une fermeture ? Comment est-elle liée à la curryfication ?
Réponses
Trop de publicités?Voici un exemple simple en JavaScript qui illustre le point:
outer = function() {
var a = 1;
var inner = function() {
alert(a);
}
return inner;
}
fnc = outer();
fnc();
Ici, j'ai défini une fonction au sein d'une fonction. La fonction interne accède à l'ensemble de la fonction externe variables locales, y compris un. La variable a est dans le champ de la fonction interne.
Normalement, quand une fonction se termine, le tout c'est de variables locales sont emportés. Cependant, si nous retournons à l'intérieur de la fonction et de l'affecter à une variable de la fnc, de sorte qu'il persiste après l'extérieur a quitté, toutes les variables dans la portée lorsque intérieure a été défini persistent également. La variable a a été fermé, c'est à l'intérieur d'une fermeture.
Notez que la variable a est totalement privé de la fnc. C'est une façon de créer des variables privées dans un langage de programmation fonctionnel tel que JavaScript.
Comme vous pourriez être en mesure de deviner, quand je l'appelle fnc (), les alertes de la valeur de a, qui est "1".
Dans une langue sans fermeture, la variable a aurait été de déchets ramassés et jetés lorsque la fonction "externe quitté. En JavaScript, la variable a persiste parce qu'il est toujours dans la portée de la fonction renvoyée fnc. C'est au sein de la fermeture.
Pourquoi est-il comme cela?
Dans un langage fonctionnel comme JavaScript où les fonctions peuvent être affectées à des variables et de circuler comme n'importe quel autre type de données, la portabilité des questions. Nous voulons que les fonctions de continuer à travailler quel que soit le contexte. Fermeture de nous donner cette capacité à se fermer sur le contexte, c'est à dire toutes les variables locales qui étaient dans la portée lorsque la fonction a été définie.
Motif De Module
Le code ci-dessus est plutôt longue haleine. Nous pouvons simplifier dans ce qui est connu dans le code JavaScript de la communauté, comme un motif de module:
(function() {
var a = 1;
var inner = function() {
alert(a);
}
window.fnc = inner;
})();
fnc();
Ce code est entièrement équivalent pour le premier exemple.
Nous déclarons tous nos variables à l'intérieur de la fermeture du local (c'est à dire des variables. Ensuite, quand nous aurons terminé, nous exposer uniquement les variables que nous voulons en les définissant comme des attributs de la fenêtre (global).
Les accolades à la fin de la déclaration de la fonction en assurer l'exécution.
Le nouvellement exposés code a accès à toutes les variables locales qui étaient dans le champ où elle a été définie. Vous trouverez exactement ce motif est utilisé dans la plupart des librairies JavaScript telles que JQuery. C'est une façon de cacher votre API comme vous le construire à l'intérieur d'une clôture, puis délibérément exposer uniquement les parties que vous voulez être consulté.
L'exposé de la fonction fnc continuera à avoir accès à toutes les variables locales (a) qui ont été dans le champ où elle a été définie.
Je vais vous donner un exemple (sur le Schéma):
(define (make-counter)
(let ((count 0))
(lambda ()
(set! count (+ count 1))
count)))
(define x (make-counter))
(x) returns 1
(x) returns 2
...etc...
Ce que cette fonction, assurez-contre, n'est il renvoie à une fonction, que nous avons appelé x, qui comptera jusqu'par un, à chaque fois son nom. Comme nous ne sommes pas fournir tous les paramètres à x, il doit en quelque sorte se rappeler le comte. Il sait où le trouver, basé sur ce qu'on appelle une portée lexicale, il faut regarder à l'endroit où elle est définie pour trouver la valeur. Cette "cachés" de la valeur est ce qui est appelé une fermeture.
Voici mon nourrissage exemple encore:
(define (add a)
(lambda (b)
(+ a b)))
(define add3 (add 3))
(add3 4) returns 7
Ce que vous pouvez voir, c'est que lorsque vous appelez ajouter avec le paramètre a (3), qui est contenue dans la fermeture de retour de la fonction nous sommes à la définition d'être add3. De cette façon, quand nous appelons add3, il sait où trouver la valeur d'effectuer l'addition.
Réponse de Kyle est assez bonne. Je pense que la clarification supplémentaire n’est que la fermeture est essentiellement un instantané de la pile au point que la fonction lambda est créée. Puis lorsque la fonction est exécutée à nouveau la pile est restaurée à cet état avant d’exécuter la fonction. Ainsi comme le mentionne Kyle, que la valeur cachée (comte) est disponible lorsque la fonction lambda s’exécute.
Afin de faciliter la compréhension de fermetures, il pourrait être utile d'examiner comment elles pourraient être mises en œuvre dans un langage procédural. Cette explication va suivre une approche simpliste de la mise en œuvre des fermetures dans le Schéma.
Pour commencer, je dois introduire le concept d'un espace de noms. Lorsque vous saisissez une commande dans un interpréteur Scheme, il faut évaluer les divers symboles dans l'expression et l'obtention de leur valeur. Exemple:
(define x 3)
(define y 4)
(+ x y) returns 7
La définition des expressions de stocker la valeur 3 à la place de x et la valeur 4 dans le spot pour y. Puis, quand nous appeler au (+ x y), l'interprète de recherche les valeurs dans l'espace de noms et est capable d'effectuer l'opération et retourner 7.
Toutefois, dans le Schéma il y a des expressions qui vous permettent de modifier temporairement la valeur d'un symbole. Voici un exemple:
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
Ce que le mot-clé let n'est introduit un nouvel espace de noms avec x la valeur 5. Vous remarquerez que c'est toujours en mesure de voir que y est de 4, ce qui rend la somme retournés à 9. Vous pouvez également voir que, une fois l'expression a terminé x est de retour pour de 3. En ce sens, x a été temporairement masquée par la valeur locale.
Procédurale et orientée objet les langues ont un concept similaire. Chaque fois que vous déclarez une variable dans une fonction qui a le même nom qu'une variable globale, vous obtenez le même effet.
Comment pourrions-nous mettre en œuvre? Un moyen simple est avec une liste chaînée - tête contient la nouvelle valeur et la queue contient l'ancien espace de noms. Lorsque vous avez besoin de rechercher un symbole, vous commencez par la tête et de travailler votre chemin vers le bas de la queue.
Maintenant, nous allons passer à la mise en œuvre de première classe de fonctions pour le moment. Plus ou moins, une fonction est un ensemble d'instructions à exécuter lorsque la fonction est appelée culminant dans la valeur de retour. Quand nous lisons dans une fonction, nous pouvons conserver ces instructions derrière les scènes et les exécuter lorsque la fonction est appelée.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns ?
Nous définissons x 3 et plus-x à son paramètre, y, plus la valeur de x. Enfin, nous appelons plus-x dans un environnement où x a été masqué par un nouveau x, celui-ci 5 évalué. Si nous nous contentons de magasin de l'exploitation (+ x y), pour la fonction plus-x, puisque nous sommes dans le contexte de x 5 le résultat retourné sera de 9. C'est ce qu'on appelle la dynamique de la portée.
Cependant, le Schéma, le Common Lisp, et de nombreuses autres langues ont ce qu'on appelle une portée lexicale, en plus de l'enregistrement de l'opération (+ x y), nous stockons l'espace de noms à ce point particulier. De cette façon, lorsque nous sommes à la recherche de la valeurs, nous pouvons voir que la valeur de x, dans ce contexte, c'est vraiment 3. C'est une clôture.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
En résumé, on peut utiliser une liste chaînée pour stocker l'état de l'espace de noms, au moment de la définition de la fonction, ce qui nous permet d'accéder aux variables de enfermant étendues, ainsi que nous offre la possibilité de localement masque d'une variable sans affecter le reste du programme.