Er zijn verschillende uitstekende artikelen die het hebben over de optimalisatie techniek die memoization heet. Je kunt ze hier en hier lezen. Wat wij gaan doen is je een kort overzicht geven van wat memoization is. We gaan een beetje verder door stap voor stap te analyseren wat “memoization” code eigenlijk doet, regel voor regel. Aan het eind van het artikel zul je memoization volledig begrijpen.
Memoization is de programmatische praktijk om lange recursieve/iteratieve functies veel sneller te laten verlopen.
Hoe, vraag je? Door de waarden die de functie retourneert na de eerste uitvoering in de cache op te slaan.
Wanneer we dezelfde waarde in onze gememoiseerde functie invoeren, retourneert deze de waarde die in de cache is opgeslagen in plaats van de functie opnieuw uit te voeren, waardoor de prestaties worden verbeterd. Niet langer hoeft uw programma elk getal opnieuw te berekenen om een resultaat te krijgen.
Klinkt geweldig, toch? Nou, wat nog beter is, is dat het niet moeilijk is om te begrijpen wat een memoize-functie doet als je de code eenmaal uit elkaar haalt.
Hier volgt een voorbeeld van een basis memo
-functie:
Laten we bij het begin beginnen…
We geven terug wat een anonieme sluiting wordt genoemd. Onze anonieme sluiting kan elke variabele of, in dit geval, functie overerven die in memo
wordt doorgegeven. We kunnen dan spelen met het argumenten object dat onze doorgegeven functie levert.
Noot terzijde: Dus, als elke functie een argumenten object heeft, waarom erven we dan niet het argumenten object van memo
? Dat komt omdat, als een regel, closures het argumenten object van een buitenfunctie niet erven. We gebruiken deze regel in ons voordeel om te spelen met de functie die we willen memoizen.
Laten we eens uiteenzetten wat memoization precies doet door te kijken naar een zeer eenvoudig en onpraktisch voorbeeld. Overigens moet je deze code, of welke code dan ook, kopiëren/plakken om echt een gevoel te krijgen hoe het werkt.
Ik denk dat we de essentie van dit codevoorbeeld wel snappen. Het spreekt bijna voor zichzelf. Laten we het wat verder uitwerken door te kijken naar een momentopname van een echte memofunctie:
Nu we hebben uitgelegd wat een memofunctie doet, kunnen we een functie-expressie maken en een recursieve Fibonacci-functie invoeren in memo
en kijken wat er gebeurt.
//Now we'll test our code!
// =======TEST=======
fib(10)//On the first try we need to load//=> loading…//=> loading…//=> loading…//=> loading…//=> loading…//=> 4 loadings later we get...//=> 89// And on the second try…fib(10)//=> 89//No loading! Yaay!//The cool thing about memoizing the recursive Fibonacci algorithm is that once we make a call for the value of the nth number in the series, we are able to store all the previous numbers in the series.//So if we try...
fib(7) //=> 21
//We don't have to recalculate anything.//And if we want to try fib(11)...
fib(11)
//loading...
//=> 144
//Our memoized recursive function already cached fib(1-10),so all it needed to do was to calculate the cached values. // This is what the cache now looks like:
/*
{ '{"0":0}': 1,
'{"0":1}': 1,
'{"0":2}': 2,
'{"0":3}': 3,
'{"0":4}': 5,
'{"0":5}': 8,
'{"0":6}': 13,
'{"0":7}': 21,
'{"0":8}': 34,
'{"0":9}': 55,
'{"0":10}': 89,
'{"0":11}': 144}
*/
Conclusie
Memoization wordt gedemystificeerd als je het terugbrengt tot sleutel-waardeparen. Het enige wat we doen is een object maken, controleren of er waarden bestaan die overeenkomen met de invoer van de gebruiker, en nieuwe sleutel-waardeparen opslaan als ze niet bestaan in ons object.
Natuurlijk betekent het opslaan van al deze gegevens dat we geheugen gaan gebruiken. Het is het beste om memoization te implementeren op functies die puur zijn en zware, repeterende berekeningen bevatten.
Bonus:
Just for fun, hier is een functionele versie van onze memoizer: