Det finns flera utmärkta artiklar som handlar om optimeringstekniken memoization. Du kan få tillgång till dem här och här. Vad vi ska göra är att ge dig en kort översikt över vad memoization är. Vi kommer att gå lite längre genom att utföra en stegvis analys av vad ”memoizing”-kod faktiskt gör, rad för rad. I slutet av artikeln kommer du att förstå memoization till fullo.

Memoization är den programmatiska metoden för att få långa rekursiva/iterativa funktioner att gå mycket snabbare.

Hur, frågar du? Genom att cachelagra de värden som funktionen returnerar efter sin första körning.

När vi matar in samma värde i vår memotiserade funktion returnerar den det värde som lagrats i cachelagret i stället för att köra funktionen igen, vilket förbättrar prestandan. Ditt program behöver inte längre räkna om varje tal för att få ett resultat.

Låter häftigt, eller hur? Tja, vad som är ännu bättre är att det inte är svårt att förstå vad en memoize-funktion gör när du bryter ner koden.

Här är ett exempel på en grundläggande memo-funktion:

Låt oss börja från början…

Vi returnerar vad som kallas för en anonym closure. Vår anonyma closure kan ärva alla variabler eller, i det här fallet, funktioner som skickas in i memo. Vi kan sedan leka med argumentobjektet som vår överlämnade funktion tillhandahåller.

Side Note: Om varje funktion har ett argumentobjekt, varför ärver vi då inte argumentobjektet i memo? Det beror på att closures som regel inte ärver en yttre funktions arguments object. Vi använder den här regeln till vår fördel för att kunna leka med den funktion vi vill memoisera.

Låt oss bryta ner exakt vad memoisering gör genom att titta på ett mycket enkelt och opraktiskt exempel. Förresten bör du kopiera/klistra in den här koden, eller vilken kod som helst för den delen, för att verkligen få en känsla för hur det fungerar.

Jag tror att vi förstår kontentan av det här kodexemplet. Det talar praktiskt taget för sig självt. Låt oss konkretisera det lite mer genom att titta på en ögonblicksbild av en riktig memofunktion:

Nu när vi har brutit ner vad en memofunktion gör, låt oss skapa ett funktionsuttryck och skicka in en rekursiv Fibonacci-funktion i memo och se vad som händer.

//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}
*/

Slutsats

Memofunktionen blir avmystifierad när man kokar ner den till nyckel-värdepar. Allt vi gör är att skapa ett objekt, kontrollera om det finns värden som matchar användarens inmatning och lagra nya nyckelvärdespar om de inte finns i vårt objekt.

Att lagra alla dessa data innebär förstås att vi kommer att använda minnet. Det är bäst att implementera memoisering på funktioner som är rena och involverar tunga, repetitiva beräkningar.

Bonus:

För skojs skull, här är en funktionell version av vår memoiserare:

Lämna ett svar

Din e-postadress kommer inte publiceras.