Wat is een sluiting?

Af en toe zie ik dat er “afsluitingen” worden genoemd, en ik heb geprobeerd het op te zoeken, maar Wiki geeft geen uitleg die ik begrijp. Kan iemand help me hier?

Reacties

  • Als je Java / C # kent, hoop dan dat deze link zal helpen- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
  • Afsluitingen zijn moeilijk te begrijpen. Je zou moeten proberen op alle links in de eerste zin van dat Wikipedia-artikel te klikken en die te begrijpen artikelen eerst.
  • stackoverflow.com/questions/36636/what-is-a-closure
  • Wat ‘ is het fundamentele verschil tussen een afsluiting en een klasse? Oké, een klasse met slechts één openbare methode.
  • @biziclop: Je zou emuleer een afsluiting met een klasse (die ‘ is wat Java-ontwikkelaars moeten doen). Maar ze ‘ zijn meestal iets minder uitgebreid te creëren en u hoeft niet je hoeft niet handmatig te beheren wat je ‘ ronddwaalt. (De hardcore lispers stellen een vergelijkbare vraag, maar komen waarschijnlijk tot die andere conclusie: dat taalniveau-OO-ondersteuning niet nodig is als je sluitingen hebt).

Antwoord

(Disclaimer: dit is een eenvoudige uitleg; wat de definitie betreft, ben ik “een beetje aan het vereenvoudigen)

De eenvoudigste manier om aan een sluiting te denken is een -functie die kan worden opgeslagen als een variabele (ook wel een “eerste -class-functie “), die een speciale mogelijkheid heeft om toegang te krijgen tot andere variabelen lokaal in het bereik waarin het is gemaakt.

Voorbeeld (JavaScript):

 var setKeyPress = function(callback) { document.onkeypress = callback; }; var initialize = function() { var black = false; document.onclick = function() { black = !black; document.body.style.backgroundColor = black ? "#000000" : "transparent"; } var displayValOfBlack = function() { alert(black); } setKeyPress(displayValOfBlack); }; initialize();  

De functies 1 toegewezen aan document.onclick en displayValOfBlack zijn sluitingen. U kunt zien dat ze allebei verwijzen naar de booleaanse variabele black, maar die variabele is toegewezen buiten de functie. Omdat black is lokaal voor het bereik waar de functie werd gedefinieerd , de pointer naar deze variabele blijft behouden.

Als je dit in een HTML-pagina plaatst:

  1. Klik om naar zwart te veranderen
  2. Druk op [enter] om “true” te zien.
  3. Klik nogmaals, verandert weer naar wit
  4. Druk op [enter] om “false” te zien.

Dit toont aan dat beide toegang hebben tot dezelfde black, en kan worden gebruikt om status op te slaan zonder een wrapper-object.

De aanroep van setKeyPress is om te demonstreren hoe een functie kan worden doorgegeven net als elke variabele. De scope bewaard in de afsluiting is nog steeds degene waarin de functie werd gedefinieerd.

Afsluitingen zijn gewoonlijk gebruikt als gebeurtenishandlers, vooral in JavaScript en ActionScript. Als u goed gebruikmaakt van sluitingen, kunt u variabelen impliciet aan gebeurtenishandlers binden zonder dat u een object-wrapper hoeft te maken. Onzorgvuldig gebruik zal echter leiden tot geheugenlekken (zoals wanneer een ongebruikte maar behouden gebeurtenishandler het enige is dat grote objecten in het geheugen vasthoudt, met name DOM-objecten, waardoor garbage collection wordt voorkomen).


1: eigenlijk zijn alle functies in JavaScript afsluitingen.

Reacties

  • Terwijl ik je antwoord las, heb ik voelde een gloeilamp in mijn hoofd aangaan. Zeer gewaardeerd! 🙂
  • Aangezien black binnen een functie wordt gedeclareerd, zou ‘ niet worden vernietigd als de stapel afwikkelt. ..?
  • @gablin, dat is het unieke aan talen met sluitingen. Alle talen met garbage collection werken op vrijwel dezelfde manier – als er geen verwijzingen meer naar een object worden gehouden, kan het worden vernietigd. Telkens wanneer een functie wordt aangemaakt in JS, is de lokale scope aan die functie gebonden totdat die functie wordt vernietigd.
  • @gablin, dat ‘ is een goede vraag. Ik denk niet dat ‘ ze kunnen ‘ t & mdash; maar ik ben pas begonnen met garbage collection sinds dat wat JS gebruikt en dat ‘ is waarnaar je leek te verwijzen toen je zei ” Sinds black wordt gedeclareerd binnen een functie, zou niet ‘ t worden vernietigd “. Onthoud ook dat als je een object declareert in een functie en het vervolgens toewijst aan een variabele die ergens anders leeft, dat object behouden blijft omdat er andere verwijzingen naar zijn.
  • Objective-C (en C onder clang) ondersteunt blokken, die in wezen sluitingen zijn, zonder garbage collection. Het vereist runtime-ondersteuning en enige handmatige tussenkomst rond geheugenbeheer.

Antwoord

Een afsluiting is eigenlijk gewoon een andere manier om naar een object te kijken. Een object zijn gegevens waaraan een of meer functies zijn gebonden. Een sluiting is een functie waaraan een of meer variabelen zijn gebonden. De twee zijn in wezen identiek, althans op implementatieniveau. Het echte verschil zit hem in waar ze vandaan komen.

Bij objectgeoriënteerd programmeren declareert u een objectklasse door vooraf de lidvariabelen en de methoden (lidfuncties) te definiëren, en vervolgens maakt u instanties van die klas. Elke instantie wordt geleverd met een kopie van de lidgegevens, geïnitialiseerd door de constructor. Je hebt dan een variabele van een objecttype en geeft deze door als een stukje data, omdat de focus ligt op de aard ervan als data.

Bij een afsluiting daarentegen is het object niet vooraf gedefinieerd als een objectklasse, of geïnstantieerd via een constructor-aanroep in uw code. In plaats daarvan schrijf je de sluiting als een functie binnen een andere functie. De sluiting kan verwijzen naar een van de lokale variabelen van de buitenste functie, en de compiler detecteert dat en verplaatst deze variabelen van de stapelruimte van de buitenste functie naar de verborgen-objectverklaring van de sluiting. Je hebt dan een variabele van het type sluiting , en hoewel het in feite een object onder de motorkap is, geef je het door als een functiereferentie, omdat de focus ligt op zijn aard als functie.

Opmerkingen

  • +1: goed antwoord. U kunt een afsluiting zien als een object met slechts één methode en een willekeurig object als een verzameling afsluitingen over enkele gemeenschappelijke onderliggende gegevens (de lidvariabelen van het object ‘ s). Ik denk dat deze twee opvattingen vrij symmetrisch zijn.
  • Zeer goed antwoord. Het verklaart eigenlijk het inzicht van afsluiting.
  • @Mason Wheeler: waar worden afsluitingsgegevens opgeslagen? In stapel als een functie? Of in heap als een object?
  • @RoboAlex: In de heap, omdat het ‘ een object is dat eruit ziet als een functie .
  • @RoboAlex: Waar een sluiting en de vastgelegde gegevens worden opgeslagen, hangt af van de implementatie. In C ++ kan het in de heap of op de stapel worden opgeslagen.

Answer

De term sluiting komt voort uit het feit dat een stuk code (blok, functie) vrije variabelen kan hebben die gesloten (dwz gebonden aan een waarde) door de omgeving waarin het codeblok is gedefinieerd.

Neem bijvoorbeeld de Scala-functiedefinitie :

def addConstant(v: Int): Int = v + k 

In de hoofdtekst van de functie zijn er twee namen (variabelen) v en k geeft twee gehele getallen aan. De naam v is gebonden omdat het wordt gedeclareerd als een argument van de functie addConstant (door naar de functiedeclaratie te kijken, weten we dat v krijgt een waarde toegewezen wanneer de functie wordt aangeroepen). De naam k is vrij ten opzichte van de functie addConstant omdat de functie geen idee bevat welke waarde k is gebonden aan (en hoe).

Om een oproep als:

val n = addConstant(10) 

te evalueren, moeten we k een waarde, die alleen kan gebeuren als de naam k is gedefinieerd in de context waarin addConstant is gedefinieerd. Bijvoorbeeld:

def increaseAll(values: List[Int]): List[Int] = { val k = 2 def addConstant(v: Int): Int = v + k values.map(addConstant) } 

Nu we addConstant hebben gedefinieerd in een context waarin k is gedefinieerd, addConstant is een sluiting geworden omdat alle de gratis variabelen zijn nu gesloten (gebonden aan een waarde): addConstant kan worden aangeroepen en doorgegeven alsof het een functie is. Let op: de gratis variabele k is gebonden aan een waarde wanneer de afsluiting is gedefinieerd , terwijl de argumentvariabele v gebonden is wanneer de afsluiting wordt aangeroepen .

Dus een sluiting is in feite een functie of codeblok dat toegang heeft tot niet-lokale waarden via zijn vrije variabelen nadat deze zijn gebonden aan de context.

In veel talen, als je gebruik een sluiting slechts als u deze anoniem kunt maken, bijv.

def increaseAll(values: List[Int]): List[Int] = { val k = 2 values.map(v => v + k) } 

Merk op dat een functie zonder vrije variabelen een speciaal geval is van een afsluiting (met een lege set vrije variabelen). Analoog is een anonieme functie een speciaal geval van een anonieme sluiting , dwz een anonieme functie is een anonieme afsluiting zonder vrije variabelen.

Reacties

  • Dit past goed bij gesloten en open formules in logica. Bedankt voor je antwoord.
  • @RainDoctor: vrije variabelen worden gedefinieerd in logische formules en in lambda-calculusuitdrukkingen op een vergelijkbare manier: de lambda in een lambda-uitdrukking werkt als een kwantor in logische formules tov vrije / gebonden variabelen .

Antwoord

Een eenvoudige uitleg in JavaScript:

var closure_example = function() { var closure = 0; // after first iteration the value will not be erased from the memory // because it is bound with the returned alertValue function. return { alertValue : function() { closure++; alert(closure); } }; }; closure_example(); 

alert(closure) gebruikt de eerder gecreëerde waarde van closure. De geretourneerde alertValue naamruimte van de functie wordt verbonden met de naamruimte waarin de variabele closure zich bevindt. Wanneer u de hele functie verwijdert, wordt de waarde van de closure variabele wordt verwijderd, maar tot die tijd kan de functie alertValue altijd de waarde van de variabele lezen / schrijven closure.

Als u deze code uitvoert, wijst de eerste iteratie een waarde 0 toe aan de closure variabele en herschrijf de functie naar:

var closure_example = function(){ alertValue : function(){ closure++; alert(closure); } } 

En omdat alertValue de lokale variabele closure om de functie uit te voeren, bindt het zichzelf met de waarde van de eerder toegewezen lokale variabele closure.

En nu elke keer dat je de closure_example -functie, wordt de verhoogde waarde van de closure variabele weggeschreven omdat alert(closure) is gebonden.

closure_example.alertValue()//alerts value 1 closure_example.alertValue()//alerts value 2 closure_example.alertValue()//alerts value 3 //etc. 

Reacties

  • bedankt, ik heb ‘ t test de code =) alles lijkt nu in orde.

Antwoord

Een “sluiting” is , in wezen een lokale staat en wat code, gecombineerd in een pakket. Typisch komt de lokale staat uit een omringende (lexicale) scope en is de code (in wezen) een innerlijke functie die vervolgens naar buiten wordt teruggestuurd. De sluiting is dan een combinatie van de vastgelegde variabelen die de innerlijke functie ziet en de code van de innerlijke functie.

Het is een van die dingen die helaas een beetje moeilijk uit te leggen is, vanwege onbekend zijn.

Een analogie die ik in het verleden met succes heb gebruikt, was stel je voor dat we iets hebben dat we het boek noemen, in de kamerafsluiting, het boek is dat exemplaar daar, in de hoek , van TAOCP, maar op de tafelafsluiting is het die kopie van een Dresden Files-boek. Dus afhankelijk van in welke afsluiting je zit, resulteert de code “geef me het boek” in verschillende dingen die gebeuren. “

Reacties

  • Dit ben je vergeten: en.wikipedia.org/wiki/Closure_(computer_programming) in uw antwoord.
  • Nee, ik heb er bewust voor gekozen om die pagina niet te sluiten.
  • ” Staat en functie. “: kan een C-functie met een static lokale variabele worden beschouwd als een afsluiting? in Haskell impliceren staat?
  • @Giorgio Sluitingen in Haskell sluiten (denk ik) de argumenten in het lexicale bereik waarin ze ‘ opnieuw zijn gedefinieerd, dus ik ‘ zeg ” ja ” (hoewel ik op zijn best niet bekend ben met Haskell). AC-functie met een statische variabele is in het beste geval een zeer beperkte sluiting (u wilt echt meerdere sluitingen kunnen maken met een enkele functie, met een static lokale variabele, u moet precies één).
  • Ik heb deze vraag met opzet gesteld omdat ik denk dat een C-functie met een statische variabele geen sluiting is: de statische variabele is lokaal gedefinieerd en alleen bekend binnen de sluiting, hij heeft geen toegang de omgeving. Ik ben ook niet 100% zeker, maar ik zou je bewering andersom formuleren: je gebruikt het sluitmechanisme om verschillende functies te creëren (een functie is een sluitingsdefinitie + een binding voor zijn vrije variabelen).

Antwoord

Het is moeilijk te definiëren wat afsluiting is zonder het concept van “staat” te definiëren.

In wezen , in een taal met volledige lexicale scoping die functies behandelt als eersteklas waarden, gebeurt er iets speciaals. Als ik zoiets zou doen als:

function foo(x) return x end x = foo 

De variabele x verwijst niet alleen naar function foo() maar het verwijst ook naar de toestand foo die de vorige keer was achtergelaten. De echte magie vindt plaats als foo andere functies heeft die verder gedefinieerd zijn binnen zijn bereik; het is als zijn eigen mini-omgeving (net zoals we “normaal” functies definiëren in een globale omgeving).

Functioneel kan het veel van dezelfde problemen oplossen als de C ++ (C?) “s” statisch “sleutelwoord, dat de status van een lokale variabele behoudt gedurende meerdere functieaanroepen; het lijkt echter meer op het toepassen van hetzelfde principe (statische variabele) op een functie, aangezien functies eersteklas waarden zijn; sluiting voegt ondersteuning toe voor de status van de hele functie die moet worden opgeslagen (niets te maken met de statische functies van C ++).

Functies behandelen als eersteklas waarden en ondersteuning toevoegen voor sluitingen betekent ook dat u meer dan één instantie van dezelfde functie in het geheugen kunt hebben (vergelijkbaar met klassen). Dit betekent dat u de dezelfde code zonder de status van de functie opnieuw in te stellen, zoals vereist is bij het omgaan met C ++ statische variabelen binnen een functie (kan dit verkeerd zijn?).

Hier is wat testen van Luas sluitingsondersteuning .

--Closure testing --By Trae Barlow -- function myclosure() print(pvalue)--nil local pvalue = pvalue or 10 return function() pvalue = pvalue + 10 --20, 31, 42, 53(53 never printed) print(pvalue) pvalue = pvalue + 1 --21, 32, 43(pvalue state saved through multiple calls) return pvalue end end x = myclosure() --x now references anonymous function inside myclosure() x()--nil, 20 x() --21, 31 x() --32, 42 --43, 53 -- if we iterated x() again 

resultaten:

nil 20 31 42 

Het kan lastig worden, en het varieert waarschijnlijk van taal naar taal, maar het lijkt in Lua dat telkens wanneer een functie wordt uitgevoerd, de status wordt gereset. Ik zeg dit omdat de resultaten van de bovenstaande code anders zouden zijn als we toegang hadden tot de functie / status direct (in plaats van via de anonieme functie die het teruggeeft), aangezien pvalue teruggezet zou worden naar 10; maar als we toegang krijgen tot de toestand van myclosure via x (de anonieme functie), kun je zien dat pvalue ergens in het geheugen nog springlevend is. Ik vermoed dat er misschien iets meer aan de hand is iemand kan de aard van de implementatie beter uitleggen.

PS: ik ken geen lik C ++ 11 (behalve wat in eerdere versies is), dus houd er rekening mee dat dit geen vergelijking is tussen sluitingen in C ++ 11 en Lua. Ook zijn alle “lijnen getrokken” van Lua naar C ++ overeenkomsten, aangezien statische variabelen en sluitingen niet 100% hetzelfde zijn; zelfs als ze soms worden gebruikt om soortgelijke problemen op te lossen.

Waar ik niet zeker van ben, is, in het codevoorbeeld hierboven, of de anonieme functie of de functie van hogere orde wordt beschouwd als de afsluiting?

Answer

Een afsluiting is een functie die een bijbehorende status heeft:

In perl maak je afsluitingen als volgt:

#!/usr/bin/perl # This function creates a closure. sub getHelloPrint { # Bind state for the function we are returning. my ($first) = @_;a # The function returned will have access to the variable $first return sub { my ($second) = @_; print "$first $second\n"; }; } my $hw = getHelloPrint("Hello"); my $gw = getHelloPrint("Goodby"); &$hw("World"); // Print Hello World &$gw("World"); // PRint Goodby World 

Als we kijken naar de nieuwe functionaliteit van C ++.
Het staat je ook toe om de huidige status aan het object te binden:

#include <string> #include <iostream> #include <functional> std::function<void(std::string const&)> getLambda(std::string const& first) { // Here we bind `first` to the function // The second parameter will be passed when we call the function return [first](std::string const& second) -> void { std::cout << first << " " << second << "\n"; }; } int main(int argc, char* argv[]) { auto hw = getLambda("Hello"); auto gw = getLambda("GoodBye"); hw("World"); gw("World"); } 

Antwoord

Laten we eens kijken naar een eenvoudige functie:

function f1(x) { // ... something } 

Deze functie wordt een functie op het hoogste niveau genoemd omdat deze “niet in een andere functie is genest. Elke JavaScript-functie koppelt aan zichzelf een lijst met objecten genaamd een ” Scope Chain “. Deze scope chain is een geordende lijst van objecten E Elke van deze objecten definieert enkele variabelen.

In functies op het hoogste niveau bestaat de scopeketen uit een enkel object, het globale object. De functie f1 hierboven heeft bijvoorbeeld een scopeketen met daarin één enkel object dat alle globale variabelen definieert. (Merk op dat de term “object” hier niet JavaScript-object betekent, het is slechts een door de implementatie gedefinieerd object dat fungeert als een variabele container, waarin JavaScript variabelen kan “opzoeken”.)

Wanneer dit functie wordt aangeroepen, maakt JavaScript iets dat een “Activeringsobject” wordt genoemd, en plaatst het bovenaan de scopeketen. object bevat alle lokale variabelen (bijvoorbeeld x hier). Daarom hebben we nu twee objecten in de scopeketen: het eerste is het activeringsobject en daaronder het globale object.

Merk heel goed op dat de twee objecten op VERSCHILLENDE tijden in de scopeketen worden geplaatst. Het globale object wordt geplaatst wanneer de functie is gedefinieerd (dwz wanneer JavaScript de functie heeft geparseerd en het functieobject heeft gemaakt), en activeringsobject komt binnen wanneer de functie wordt aangeroepen.

Dus we weten nu dit:

  • Aan elke functie is een bereikketen gekoppeld
  • Wanneerde functie is gedefinieerd (wanneer het functieobject wordt gemaakt), slaat JavaScript een scopeketen op met die functie
  • Voor functies op het hoogste niveau bevat de scopeketen alleen het globale object op het moment van de functiedefinitie en voegt een extra activeringsobject bovenaan op aanroeptijd

De situatie wordt interessant als we te maken hebben met geneste functies. Laten we er dus een maken:

function f1(x) { function f2(y) { // ... something } } 

Wanneer f1 wordt gedefinieerd, krijgen we een bereikketen ervoor met alleen het globale object.

Wanneer nu f1 wordt aangeroepen, krijgt de scopeketen van f1 het activeringsobject. Dit activeringsobject bevat de variabele x en de variabele f2 die een functie is. Merk op dat f2 wordt gedefinieerd.Daarom slaat JavaScript op dit punt ook een nieuwe scopeketen op voor f2. De scoopketen die is opgeslagen voor deze innerlijke functie is de huidige scopeketen die van kracht is. De huidige scope in feite is de keten die van f1 “s. Daarom is de bereikketen van f2f1 “s huidige bereikketen – die het activeringsobject bevat van f1 en het globale object.

Wanneer f2 wordt aangeroepen, krijgt het zijn eigen activeringsobject met y, toegevoegd aan de scope-keten die al het activeringsobject van f1 en het globale object bevat.

Als er een andere geneste functie is gedefinieerd binnen f2, zou de scope-keten drie objecten bevatten op het moment van de definitie (2 activeringsobjecten van twee buitenste functies en het globale object), en 4 op het moment van aanroep.

Dus, nu onderschrijven we tand hoe de scope-keten werkt, maar we hebben het nog niet over sluitingen gehad.

De combinatie van een functieobject en een scope (een set variabele bindingen) waarin de variabelen van de functie worden opgelost, wordt een afsluiting genoemd in de informatica-literatuur – JavaScript de definitieve gids door David Flanagan

De meeste functies worden aangeroepen met dezelfde scopeketen die van kracht was toen de functie werd gedefinieerd, en het maakt niet echt uit dat er een sluiting bij betrokken is. Afsluitingen worden interessant wanneer ze worden aangeroepen onder een andere scopeketen dan degene die van kracht was toen ze werden gedefinieerd. Dit gebeurt meestal wanneer een genest functieobject geretourneerd is vanuit de functie waarin het is gedefinieerd.

Wanneer de functie terugkeert, wordt dat activeringsobject verwijderd uit de scopeketen. Als er geen geneste functies zijn, zijn er geen verwijzingen meer naar het activeringsobject en wordt het afval verzameld. Als er geneste functies zijn gedefinieerd, dan heeft elk van die functies een verwijzing naar de scopeketen, en die scopeketen verwijst naar het activeringsobject.

Als die geneste functieobjecten echter binnen hun buitenste functie bleven, dan worden ze zelf vuilnis opgehaald, samen met het activeringsobject waarnaar ze verwezen. Maar als de functie een geneste functie definieert en deze retourneert of ergens in een eigenschap opslaat, dan is er een externe verwijzing naar de geneste functie. Het zal niet als garbage worden opgehaald, en het activeringsobject waarnaar het verwijst, zal ook niet als garbage worden opgehaald.

In ons bovenstaande voorbeeld retourneren we niet f2 van f1, dus als een aanroep van f1 terugkeert, wordt het activeringsobject verwijderd uit de bereikketen en wordt het vuilnis opgehaald. Maar als we zoiets als dit hadden:

function f1(x) { function f2(y) { // ... something } return f2; } 

Hier zal de terugkerende f2 een bereikketen hebben die bevatten het activeringsobject f1, en daarom wordt het niet “garbage collection”. Als we op dit moment f2 aanroepen, heeft het toegang tot de variabele f1 “s x ook al zijn we “out of f1.

Vandaar dat we kunnen zien dat een functie zijn scopeketen bij zich houdt en bij de scopeketen komen alle activeringsobjecten van uiterlijke functies. Dit is de essentie van afsluiting. We zeggen dat functies in JavaScript “lexicaal bereik” , wat betekent dat ze het bereik dat actief was toen ze werden gedefinieerd, opslaan in tegenstelling tot het bereik dat actief was toen ze werden aangeroepen.

Er zijn een aantal krachtige programmeertechnieken waarbij sluitingen betrokken zijn, zoals het benaderen van privévariabelen , gebeurtenisgestuurd programmeren, gedeeltelijke toepassing , enz.

Merk ook op dat dit allemaal van toepassing is op al die talen die sluitingen ondersteunen. Bijvoorbeeld PHP (5.3+), Python, Ruby, etc.

Antwoord

Een afsluiting is een compileroptimalisatie (ook bekend als syntactische suiker?). Sommige mensen noemen dit ook het Poor Man “s Object .

Zie het antwoord van Eric Lippert : (fragment hieronder)

De compiler zal de volgende code genereren:

 private class Locals { public int count; public void Anonymous() { this.count++; } } public Action Counter() { Locals locals = new Locals(); locals.count = 0; Action counter = new Action(locals.Anonymous); return counter; }  

Klopt het?
U vroeg ook om vergelijkingen. VB en JScript maken beide sluitingen op vrijwel dezelfde manier.

Opmerkingen

  • Dit antwoord is een CW omdat ik geen ‘ punten verdien voor Eric ‘ s geweldige antwoord.Stem het op naar eigen inzicht. HTH
  • -1: Uw uitleg is te root in C #. Sluiting wordt in veel talen gebruikt en is veel meer dan syntactische suiker in deze talen en omvat zowel de functie als de staat.
  • Nee, een sluiting is niet zomaar een ” compileroptimalisatie ” noch syntactische suiker. -1

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *