Question

J'ai constaté que de nombreuses personnes utilisent les mots fermeture et bloc de manière interchangeable. La plupart de ces personnes ne peuvent pas expliquer de quoi elles parlent.

Certains programmeurs Java (même ceux de sociétés de conseil très onéreuses) parlent de classes internes anonymes sous la forme de "blocs". et " fermetures " - mais je sais que ce n'est pas vrai. (Vous ne pouvez pas transmettre de variables mutables à partir de la portée de la méthode dans laquelle elles ont été définies ...)

Je cherche:

  • une définition informatique précise d'un bloc
  • une définition informatique précise d'une fermeture
  • et clarification sur la différence entre les deux.

J'aimerais vraiment voir des liens, des articles ou des ouvrages sur ces références s'il vous plaît .

Était-ce utile?

La solution

Bien qu'un bloc ne soit qu'un morceau de code pouvant être composé d'instructions et de déclarations, mais rien d'autre, une fermeture est un véritable objet de première classe, variable dont la valeur est un bloc.

La principale différence est qu'un bloc regroupe simplement des instructions (par exemple, le corps d'une instruction while ), alors qu'une fermeture est une variable contenant du code pouvant être exécuté.

Si vous avez une fermeture, vous pouvez généralement la transmettre en tant que paramètre aux fonctions, la freiner et la décurrifier, et l’appeler principalement!

Closure c = { println 'Hello!' }
/* now you have an object that contains code */
c.call()

Bien sûr, les fermetures sont plus puissantes, elles sont des variables et peuvent être utilisées pour définir un comportement personnalisé des objets (alors que vous deviez généralement utiliser des interfaces ou d'autres approches de programmation orientée objet).

Vous pouvez imaginer une fermeture comme une fonction contenant ce que cette fonction fait en elle-même.

Les blocs sont utiles car ils permettent de définir la portée des variables. Généralement, lorsque vous définissez une variable dans une étendue, vous pouvez remplacer les définitions externes sans aucun problème et de nouvelles définitions existeront lors de l'exécution du bloc.

for (int i = 0; i < 10; ++i)
{
     int t = i*2;
     printf("%d\r\n", t);
}

t est défini à l'intérieur du bloc (le corps de l'instruction pour ) et durera juste à l'intérieur de ce bloc.

Autres conseils

Un bloc est quelque chose de syntaxique - Une unité logique d'instructions (plus liée à portée qu'à clôture ).

if (Condition) {
    // Block here 
} 
else {
    // Another block
}

Une fermeture est liée à des fonctions ou à des classes anonymes - Un objet (fonction) anonyme, un morceau de code lié à un environnement (avec ses variables).

def foo() {
   var x = 0
   return () => { x += 1; return x }
}

Ici toto renvoie une fermeture! La variable locale x persiste jusqu'à la fermeture même après la fin de toto et peut être incrémentée via les appels de la fonction anonyme renvoyée.

val counter = foo()
print counter() // Returns 2
print counter() // Return 3

Notez que c'est juste Ruby dans lequel bloc et fermeture sont traités de manière similaire puisque ce que Ruby appelle un bloc est une fermeture:

(1..10).each do |x|
    p x
end

Il y a dans chaque méthode une fonction de fermeture (prenant un paramètre x) appelée un bloc en Ruby.

Il y a beaucoup de confusion ici, car il y a des termes avec plusieurs définitions et plusieurs choses distinctes qui sont fusionnées simplement parce qu'elles se trouvent généralement ensemble.

Premièrement, nous avons "bloc". C'est juste un morceau de code lexical qui fait une unité - le corps d'une boucle, par exemple. Si le langage a réellement une portée de bloc, il est alors possible de définir des variables qui n'existent que dans cette partie de code.

Deuxièmement, nous avons le code appelable comme type de valeur. Dans les langages fonctionnels, ce sont des valeurs de fonction, parfois appelées "funs", "fonctions anonymes". (parce que la fonction est trouvée dans la valeur, pas le nom auquel elle est assignée; vous n'avez pas besoin d'un nom pour les appeler), ou "lambdas" (de l'opérateur utilisé pour les créer dans le Calcul Lambda de Church). On peut les appeler "fermetures", mais ce ne sont pas automatiquement des fermetures vraies; pour pouvoir se qualifier, ils doivent encapsuler ("fermer sur") la portée lexicale entourant leur création, c'est-à-dire que les variables définies en dehors de la portée de la fonction elle-même mais dans le cadre de sa définition sont toujours disponibles chaque fois que la fonction est appelée, même si le point d’appel se situe après que la variable référencée aurait autrement été hors de portée et que son stockage aurait été recyclé.

Par exemple, considérons le code Javascript suivant:

function makeClosure() {
  var x = "Remember me!";
  return function() {
    return "x='" + x + "'";
  }
}

// console.log(x); 
// The above is an error; x is undefined
var f = makeClosure();
console.log(f());
// The above outputs a string that includes x as it existed when f was created.

La variable x est définie uniquement dans le corps de la fonction makeClosure ; en dehors de cette définition, il n'existe pas. Après avoir appelé makeClosure , le x déclaré à l'intérieur de celui-ci devrait avoir disparu. Et c'est le cas, du point de vue de la plupart des codes. Mais la fonction renvoyée par makeClosure a été déclarée alors que x existait, de sorte qu'il y a toujours accès lorsque vous l'appelez plus tard. Cela en fait une véritable fermeture.

Vous pouvez avoir des valeurs de fonction qui ne sont pas des fermetures, car elles ne préservent pas l'étendue. Vous pouvez également avoir des fermetures partielles; Les valeurs des fonctions PHP ne conservent que des variables spécifiques qui doivent être listées au moment de la création de la valeur.

Vous pouvez également avoir des valeurs de code appelables qui ne représentent pas du tout des fonctions. Smalltalk appelle ces "fermetures de blocs", tandis que Ruby les appelle "procs", bien que de nombreux Rubyists les appellent simplement "blocs", car ils sont la version réifiée de ce qui est créé par le {. .. } ou do ... fin . Ce qui les distingue des lambdas (ou "fermetures de fonctions") est qu’ils n’introduisent pas un nouveau niveau d’appel. Si le code dans le corps d'une fermeture de bloc appelle return , il renvoie depuis la fonction / méthode externe la fermeture de bloc existante, pas seulement le bloc lui-même.

Ce comportement est essentiel pour conserver ce que R.D. Tennent a appelé le "principe de correspondance", qui stipule que vous devriez pouvoir remplacer tout code par une fonction en ligne contenant ce code dans le corps et appelée immédiatement. Par exemple, en Javascript, vous pouvez remplacer ceci:

x=2
console.log(x)

avec ceci:

(function(){x = 2;})();
console.log(x)

Cet exemple n’est pas très intéressant, mais la possibilité de réaliser ce type de transformation sans affecter le comportement du programme joue un rôle essentiel dans la refactorisation fonctionnelle. Mais avec lambdas, dès que vous avez incorporé les instructions return , le principe n'est plus valable:

function foo1() {
  if (1) {
    return;
  }
  console.log("foo1: This should never run.")
}
foo1()
function foo2() {
  if (1) {
    (function() { return; })();
  }
  console.log("foo2: This should never run.")
}
foo2()

La seconde fonction est différente de la première; le console.log est exécuté, car le return ne renvoie que de la fonction anonyme, pas de foo2 . Cela brise le principe de correspondance.

C'est pourquoi Ruby a à la fois des procs et des lambdas, même si la distinction est une source de confusion perpétuelle pour les débutants. Les deux procs et lambdas sont des objets de classe Proc , mais ils se comportent différemment, comme indiqué ci-dessus: un return retourne simplement du corps d'un lambda, mais de la méthode entourant un proc.

def test
  p = proc do return 1 end
  l = lambda do return 1 end
  r = l[]
  puts "Called l, got #{r}, still here."
  r = p[]
  puts "Called p, got #{r}, still here?"
end

La méthode test ci-dessus ne parviendra jamais à la deuxième met , car l'invocation de p provoquera test pour retourner immédiatement (avec une valeur de retour de 1). Si Javascript avait des blocs fermés, vous pourriez faire la même chose, mais ce n’est pas le cas (bien qu’il soit proposé de les ajouter).

Le barbu bruyant a ceci à dire sur les fermetures et les blocs:

http://martinfowler.com/bliki/Closure.html

À un moment donné, il dit qu'une fermeture est un bloc qui peut être passé comme argument à une méthode.

Les termes que vous utilisez sont les plus couramment utilisés ensemble ces jours-ci en Ruby, bien que les constructions apparaissaient auparavant dans Algol, Smalltalk et Scheme. Je citerais le standard Ruby, s’il en existait un.

Je ne suis pas sûr de pouvoir répondre à votre question exacte, mais je peux illustrer. Toutes mes excuses si vous le savez déjà ...

def f &x
  yield
  x
end

def g
  y = "block"
  t = f { p "I'm a #{y}" }
  y = "closure"
  t
end

t = g
t.call

Et ...

$ ruby exam.rb
"I'm a block"
"I'm a closure"
$ 

Un bloc est donc une séquence de code anonyme, semblable à une fonction, attachée à un appel de méthode. Il est utilisé partout dans l'API Ruby. Lorsque vous facilitez suffisamment la tâche pour créer une fonction anonyme, elle s'avère utile pour toutes sortes de choses.

Mais notez qu'après le retour de f , puis de g , nous avons conservé le bloc en le renvoyant à partir de f (sous forme de x ) puis à partir de g (sous forme de t ). Maintenant, nous appelons le bloc une seconde fois. De nouveau, notez que g () a été renvoyé. Mais le bloc fait référence à une variable locale dans une instance de fonction (et une portée) qui n’existe plus?! Et il obtient la nouvelle valeur de y ?!

Une fermeture est donc un objet de type fonction fermé sur son étendue lexicale. Leur mise en oeuvre est assez difficile car ils détruisent le modèle de type pile-à-pile qui est si utile pour les variables locales dans les instances d'appel de fonction.

1. Ruby a différentes saveurs d'objets de fonction de type fermeture; C'est juste l'un d'eux.

  

5

Il s’agit d’un entier .

  

Int workDaysInAWeek = 5

Il s’agit d’une variable entière et elle peut être définie sur un entier différent . (Si les circonstances vous empêchent de modifier cette valeur, vous pouvez l'appeler une constante .)

Alors que les numéros concernant les problèmes ci-dessus, les blocs et les fermetures concernent les algorithmes. La distinction entre les blocs et les fermetures , respectivement, est également équivalente à la précédente.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top