Ya hemos visto como usar la memoization para mejorar el rendimiento del código memorizando los resultados de las llamadas a funciones. sin embargo este sistema tiene un punto débil. Las funciones recursivas. Veamos un ejemplo con la función factorial:
function factorial(n){
console.log("factorial "+n);
if(n < 2){
return 1;
} else {
return n*factorial(n-1);
}
}
Ahora crearemos un objeto Memo donde almacenar las llamadas y los resultados:
let memof = new Memo(factorial);
memof.call(4);
factorial 4
factorial 3
factorial 2
factorial 1
24
memof.call(4);
24
memof.call(5);
factorial 5
factorial 4
factorial 3
factorial 2
factorial 1
120
Se puede ver que tras calcular el valor de factorial(4), cuando se llama a factorial(5) este vuelve a llamar a factorial con valores que factorial(4) ya ha llamado y que no han sido memorizadas. Esto se debe a que dentro de la función factorial se llama a factorial(n-1) en lugar de a memof.call(n-1) por lo que esa llamada no pasa por el proceso de memorización y no se guardan esos resultados. Esto le quita mucha eficacia a la memoization. Sin embargo hay un truco que no es muy elegante pero que puede ayudarnos.
Cuando declaras una función en javascript es como si declararas una variable y le asignaras una función:
function factorial(n){
...
}
Es lo mismo que:
let factorial = function(n){
...
}
Que pasaría si le asignáramos a la variable factorial otra función distinta, pues que el código que llama a factorial llamaría ahora a esa nueva función. Y si esa función llama a memof.call() las llamadas recursivas se memorizarían:
factorial = (n) => memof.call(n);
factorial(4);
factorial 4
factorial 3
factorial 2
factorial 1
24
factorial(4);
24
factorial(5);
factorial 5
120
Pero si la función factorial ya no apunta al código de factorial ¿Como es que sigue funcionando y calculando el valor del factorial?. La respuesta es que la función original sigue referenciada:
class Memo {
constructor (func, keyFunc) {
this.func = func;
this.cache = {};
this.calculateKey = keyFunc || this.calculateKey;
}
....
}
El código anterior sigue siendo accesible desde la variable func de la clase memo.
Podéis encontrar todo el código y ejemplos en la librería memo.js