37 votes

Quand utiliser la fermeture?

J'ai vu des échantillons de fermeture - http://stackoverflow.com/questions/36636/what-is-a-closure

Quelqu'un peut-il fournir de l'exemple simple d'utilisation de la fermeture?

Plus précisément, les scénarios dans lesquels la fermeture du sens?

Supposons que la langue n'a pas de soutien de fermeture, comment pourrait-on encore obtenir le même chose?

De ne pas offenser quiconque, s'il vous plaît poster des exemples de code dans un langage comme c#, python, javascript, ruby, etc.
Je suis désolé, je ne comprends pas les langages fonctionnels encore.

32voto

Marc Gravell Points 482669

Les fermetures sont tout simplement excellents outils. Quand les utiliser? Tout moment vous comme... Comme cela a déjà été dit, l'alternative est d'écrire une classe; par exemple, avant de C# 2.0, la création d'un paramétrer fil a été un véritable combat. Avec C# 2.0, vous n'avez même pas besoin de la "ParameterizedThreadStart' que tu viens de faire:

string name = // blah
int value = // blah
new Thread((ThreadStart)delegate { DoWork(name, value);}); // or inline if short

Comparez cela à la création d'une classe avec un nom et une valeur

Ou aussi avec la recherche d'une liste (à l'aide d'un lambda, cette fois):

Person person = list.Find(x=>x.Age > minAge && x.Region == region);

Nouveau - l'alternative serait d'écrire une classe avec deux propriétés et une méthode:

internal sealed class PersonFinder
{
    public PersonFinder(int minAge, string region)
    {
        this.minAge = minAge;
        this.region = region;
    }
    private readonly int minAge;
    private readonly string region;
    public bool IsMatch(Person person)
    {
        return person.Age > minAge && person.Region == region;
    }
}
...
Person person = list.Find(new PersonFinder(minAge,region).IsMatch);

C'est assez comparable à la façon dont le compilateur t-il sous le capot (en fait, il utilise en lecture/écriture publique des champs, pas de private readonly).

Le plus grand inconvénient avec C# capture est de regarder le champ d'application; par exemple:

        for(int i = 0 ; i < 10 ; i++) {
            ThreadPool.QueueUserWorkItem(delegate
            {
                Console.WriteLine(i);
            });
        }

Cela pourrait ne pas imprimer ce que vous attendez, car la variable i est utilisée pour chaque. Vous avez pu voir n'importe quelle combinaison de répétitions - même 10 10. Vous devez soigneusement la portée capturé les variables en C#:

        for(int i = 0 ; i < 10 ; i++) {
            int j = i;
            ThreadPool.QueueUserWorkItem(delegate
            {
                Console.WriteLine(j);
            });
        }

Ici, chaque j est capturé séparément (p. ex. les différentes généré par le compilateur instance de classe).

Jon Skeet a une bonne entrée de blog couvrant C# et java fermetures ici; ou, pour plus de détails, voir son livre C# en Profondeur, ce qui a un chapitre entier sur eux.

22voto

Brian Points 82719

Je suis d'accord avec la réponse précédente de "tous les temps". Quand vous programme dans un langage fonctionnel ou dans la langue où les lambdas et les fermetures sont en commun, de les utiliser sans même s'en apercevoir. C'est comme demander "qu'est-ce que le scénario d'une fonction?" ou "qu'est-ce que le scénario d'une boucle?" Ce n'est pas de faire de la question d'origine de son mutisme, c'est plutôt pour signaler qu'il y a des constructions dans les langues que vous n'avez pas de définir en termes de scénarios spécifiques. Vous venez de les utiliser tout le temps, pour tout, il est une seconde nature.

C'est en quelque sorte rappelle:

Le vénérable maître Qc Na marchait avec son élève, Anton. En espérant invite le maître dans une discussion, Anton a dit "Maître, j'ai entendu dire que les objets sont une très bonne chose - est - ce vrai?" Qc Na regardé par un mouvement de pitié à son élève et lui répondit, "Stupide l'élève, les objets ne sont que des pauvres l'homme de fermetures d'usines."

Châtié, Anton a pris son congé de son maître et retourna à sa cellule, l'intention de l'étude de fermetures. Il lire attentivement l'intégralité de cette "Lambda: L' Ultime..." série d'articles et de ses cousins, et mis en place une petite Interpréteur Scheme avec un fermeture d'objets basé sur le système. Il beaucoup appris, et attend avec impatience informer son maître de ses progrès.

Sur son prochain marcher avec Qc Na, Anton tentative pour impressionner son maître par en disant: "Maître, j'ai diligence étudié la question, et maintenant comprendre que les objets sont vraiment un pauvre homme fermetures." Qc Na répondu en frappant Anton avec son bâton, en disant: "Quand allez-vous apprendre? Les fermetures sont mauvais l'homme est l'objet." À ce moment, Anton est devenu éveillé.

(http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html)

14voto

zvoase Points 650

Le plus simple exemple de l'utilisation de fermetures est dans quelque chose qui s'appelle nourrissage. En gros, imaginons que nous avons une fonction f() qui, lorsqu'elle est appelée avec deux arguments a et b, les additionne. Donc, en Python, nous avons:

def f(a, b):
    return a + b

Mais disons que, pour la clarté de l'exposé, que nous voulons seulement faire appel f() avec un argument à la fois. Ainsi, au lieu d' f(2, 3), nous voulons f(2)(3). Cela peut être fait comme suit:

def f(a):
    def g(b): # Function-within-a-function
        return a + b # The value of a is present in the scope of g()
    return g # f() returns a one-argument function g()

Maintenant, quand nous appelons f(2), nous obtenons une nouvelle fonction, g(); cette nouvelle fonction, qui comporte des variables à partir de la portée de l' f(), et il est dit proximité plus de ces variables, d'où le terme de fermeture. Lorsque nous appelons g(3), la variable a (qui est liée par la définition d' f) est accessible en g(), revenant 2 + 3 => 5

Ceci est utile dans plusieurs scénarios. Par exemple, si j'avais une fonction qui a accepté un grand nombre d'arguments, mais seulement quelques-uns d'entre eux ont été utile pour moi, je pourrais écrire une fonction générique de la sorte:

def many_arguments(a, b, c, d, e, f, g, h, i):
    return # SOMETHING

def curry(function, **curry_args):
    # call is a closure which closes over the environment of curry.
    def call(*call_args):
        # Call the function with both the curry args and the call args, returning
        # the result.
        return function(*call_args, **curry_args)
    # Return the closure.
    return call

useful_function = curry(many_arguments, a=1, b=2, c=3, d=4, e=5, f=6)

useful_function est maintenant une fonction qui a besoin seulement de 3 arguments, au lieu de 9. - Je éviter d'avoir à me répéter, et ont également créé un générique solution; si j'écris l'autre beaucoup d'argument de la fonction, je peux utiliser l' curry outil nouveau.

11voto

Rich Points 2429

En règle générale, si l'on ne dispose pas de fermetures, on doit définir une classe à l'équivalent de la fermeture de l'environnement, et de les passer.

Par exemple, dans un des langages comme Lisp, on peut définir une fonction qui retourne une fonction (fermée sur l'environnement) pour ajouter un certain montant prédéfini à son argument ainsi:

(defun make-adder (how-much)
  (lambda (x)
    (+ x how-much)))

et de l'utiliser comme ceci:

cl-user(2): (make-adder 5)
#<Interpreted Closure (:internal make-adder) @ #x10009ef272>
cl-user(3): (funcall * 3)     ; calls the function you just made with the argument '3'.
8

Dans une langue sans fermetures, il vous faudrait faire quelque chose comme ceci:

public class Adder {
  private int howMuch;

  public Adder(int h) {
    howMuch = h;
  }

  public int doAdd(int x) {
    return x + howMuch;
  }
}

et puis l'utiliser comme ceci:

Adder addFive = new Adder(5);
int addedFive = addFive.doAdd(3);
// addedFive is now 8.

La fermeture contient implicitement son environnement avec elle; vous de manière transparente reportez-vous à l'environnement que de l'intérieur de l'exécution de la partie (le lambda). Sans fermetures vous devez faire que de l'environnement explicite.

Qu'il faut expliquer à vous lorsque vous utilisez fermetures: tout le temps. La plupart des cas où une classe est instanciée à un certain état d'une autre partie du calcul et de l'appliquer ailleurs sont élégamment remplacés par des fermetures d'usines dans les langues qui les soutiennent.

On peut mettre en œuvre un objet avec système de fermeture.

3voto

Martin v. Löwis Points 61768

Voici un exemple de Python standard library, inspect.py. Actuellement, il lit

def strseq(object, convert, join=joinseq):
    """Recursively walk a sequence, stringifying each element."""
    if type(object) in (list, tuple):
        return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object))
    else:
        return convert(object)

Cela a, en tant que paramètres, une fonction de conversion et une fonction join, et, récursivement, les promenades sur les listes et les tuples. La récursivité est mis en œuvre à l'aide de la carte(), où le premier paramètre est une fonction. Le code est antérieure à l'appui pour les fermetures en Python, il faut donc deux autres arguments par défaut, pour passer de convertir et de se joindre à l'appel récursif. Avec les fermetures, ce lit

def strseq(object, convert, join=joinseq):
    """Recursively walk a sequence, stringifying each element."""
    if type(object) in (list, tuple):
        return join(map(lambda o: strseq(o, convert, join), object))
    else:
        return convert(object)

Dans les langages à objets, vous n'avez généralement pas utiliser des fermetures trop souvent, que vous pouvez utiliser les objets pour passer de l'état et des méthodes liées, lorsque votre langue que la leur. Quand Python n'ai pas de fermetures, les gens ont dit que Python émule fermetures avec des objets, alors que Lisp émule des objets avec des fermetures. Comme un exemple à partir du RALENTI (ClassBrowser.py):

class ClassBrowser: # shortened
    def close(self, event=None):
        self.top.destroy()
        self.node.destroy()
    def init(self, flist):
        top.bind("<Escape>", self.close)

Ici, l'auto.la fermeture est un paramètre moins de callback appelée lorsque la fuite est pressé. Cependant, la mise en œuvre n'a besoin de paramètres - à savoir l'auto, et puis l'auto.en haut, de soi.nœud. Si Python n'ont pas les méthodes liées, vous pouvez écrire

class ClassBrowser:
    def close(self, event=None):
        self.top.destroy()
        self.node.destroy()
    def init(self, flist):
        top.bind("<Escape>", lambda:self.close())

Ici, le lambda serait d'obtenir le "soi" ne pas partir d'un paramètre, mais à partir du contexte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X