42 votes

Mémoriser une fonction currifiée

const f = (arg1) => (arg2) => { /* returns something */ }

Est-il possible de mémoriser f en ce qui concerne les 2 arguments, à savoir :

f(1)(2);
f(1)(3); // Référence cache non utilisée
f(4)(2); // Référence cache non utilisée
f(1)(2); // Référence cache utilisée

1 votes

Ma supposition est que vous devriez ajouter une couche différente qui mettrait en cache les appels....

1 votes

Vérifiez la bibliothèque reselect. devrait faire ce que vous voulez directement.

1 votes

Pourquoi faut-il le currifier, cela semble être une abstraction inutile.

63voto

Nina Scholz Points 17120

Vous pourriez prendre une Map comme cache et prendre des maps imbriquées pour tous les arguments suivants.

Ce cache fonctionne pour un nombre arbitraire d'arguments et réutilise les valeurs des appels précédents.

Il fonctionne en prenant une fonction curryfiée et une Map facultative. Si la map n'est pas fournie, une nouvelle map est créée qui sert de cache de base pour tous les autres appels de la fermeture retournée ou le résultat final.

La fonction interne prend un seul argument et vérifie si cette valeur est dans la map.

  • Si ce n'est pas le cas, appelez la fonction curryfiée et vérifiez la valeur retournée

    • si c'est une fonction, créez une nouvelle fermeture sur la fonction et une nouvelle map,

    • si ce n'est pas une fonction, prenez le résultat,

    comme valeur pour un nouvel élément de la map.

  • Enfin, retournez la valeur de la map.

    const cached = (fn, map = new Map()) => arg => { const inCache = map.has(arg); const hint = inCache ? 'dans le cache' : 'pas dans le cache';

    console.log(arg, hint);
    
    if (!inCache) {
        const value = fn(arg);
        const result = typeof value === 'function' ? cached(value, new Map()) : value;
    
        map.set(arg, result);
    }
    
    return map.get(arg);

    };

    const f = a => b => c => a b c; // la fonction curryfiée d'origine const g = cached(f); // sa variante mise en cache

    console.log(g(1)(2)(5)); // pas pas pas 10 console.log(g(1)(3)(4)); // dans pas pas 12 console.log(g(4)(2)(3)); // pas pas pas 24 console.log(g(1)(2)(6)); // dans dans pas 12 console.log(g(4)(2)(3)); // dans dans dans 24

    .as-console-wrapper { max-height: 100% !important; top: 0; }

2voto

Mark Meyer Points 31911

Question intéressante — vous pourriez avoir des caches indépendants pour chaque fonction. Le cache sur la fonction extérieure contiendra des fonctions. Chaque fonction intérieure pourrait avoir son propre cache indépendant. Ainsi, en appelant f(10)(1) suivi de f(10)(2) entraînerait l'appel d'une version mise en cache de la fonction intérieure. Appeler à nouveau f(10)(1) toucherait les deux caches :

function getCachedF() {
  // le cache extérieur contient des fonctions indexées par argument
  let outer_memo = {}  

  const f = (arg1) => {
    if (!outer_memo.hasOwnProperty(arg1)) {
      // Créer une fonction interne dans le cache extérieur
      // chaque fonction interne a besoin de son propre cache
      // car elle renverra des valeurs différentes
      // en fonction des appels de la fonction extérieure
      let inner_memo = {}                  
      console.log("cache extérieur vide")

      outer_memo[arg1] = (arg2) => {
        // une fonction memoized normale
        // le cache est une paire de clé:valeur simple
        if (!inner_memo.hasOwnProperty(arg2)) {
          console.log("cache intérieur vide")
          inner_memo[arg2] = arg1 + arg2
        }
        return inner_memo[arg2]
      }
    }
    return outer_memo[arg1]
  }
  return f
}

let f = getCachedF()
// les deux caches sont vides
console.log("3+5", f(3)(5))

// résultat mis en cache
console.log("3+5", f(3)(5))

// seulement le cache intérieur est utilisé
console.log("3+8", f(3)(8))

// le cache interne n'est utilisé que si les deux arguments sont les mêmes
console.log("10+8", f(10)(8))

Une autre alternative serait d'avoir un seul cache avec des clés qui sont une combinaison des deux arguments, mais alors la fonction interne devrait toujours être appelée.

0voto

SashaSemanyuk Points 178

Vous ne pouvez pas passer la carte à chaque fonction. Vous pouvez faire comme suit :

const memoize = fn => {
  const cache = {};
  return (...args) => {
    const curriedFn = fn(...args);
    return (...next) => {
      const key = // générer votre clé
      if (key in cache) return cache[key];
      return (cache[key] = curriedFn(...next));
    }
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X