Запоминать закриченную функцию

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

Можно ли запомнить f относительно 2 аргументов, а именно:

f(1)(2);
f(1)(3); // Cache not hit
f(4)(2); // Cache not hit
f(1)(2); // Cache hit

Ответы

Ответ 1

Вы можете взять Map качестве кэша и использовать вложенные карты для всех следующих аргументов.

Этот кеш работает для произвольного количества аргументов и повторно использует значения из предыдущих вызовов.

Это работает, беря карри функцию и опциональную Map. Если карта не указана, создается новая карта, которая служит базовым кэшем для всех других вызовов возвращаемого замыкания или конечного результата.

Внутренняя функция принимает один аргумент и проверяет, есть ли это значение на карте.

  • Если нет, вызовите карри функцию и проверьте возвращаемое значение

    • если функция, создайте новое замыкание над функцией и новую карту,

    • если функция не принимает результат,

    в качестве значения для нового элемента карты.

  • Наконец, верните значение с карты.

const
    cache = (fn, map = new Map) => arg => {
        console.log(arg, map.has(arg) ? 'in cache' : 'not in cache');
        if (!map.has(arg)) {
            var value = fn(arg);
            map.set(arg, typeof value === 'function' ? cache(value, new Map) : value);
        }
        return map.get(arg);
    },        
    f = a => b => c => a * b * c, // curried function
    g = cache(f);                 // cache function f, return function with closure

console.log(g(1)(2)(5)); // not not not 10
console.log(g(1)(3)(4)); //  in not not 12
console.log(g(4)(2)(3)); // not not not 24
console.log(g(1)(2)(6)); //  in  in not 12
console.log(g(4)(2)(3)); //  in  in  in 24
.as-console-wrapper { max-height: 100% !important; top: 0; }

Ответ 2

Интересный вопрос - вы можете иметь независимые кэши для каждой функции. Кэш на внешней функции будет содержать функции. Каждая внутренняя функция может получить свой независимый кеш. Таким образом, вызов f(10)(1) за которым следует f(10)(2), приведет к вызову кэшированной версии внутренней функции. Вызов f(10)(1) снова вызовет оба кэша:

function getCachedF() {
  // outer cache holds functions keyed to argument
  let outer_memo = {}  
                
  const f = (arg1) => {
    if (!outer_memo.hasOwnProperty(arg1)) {
      // Create inner function on outer cache
      // each inner function needs its own cache
      // because it will return different values
      // given different outer function calls
      let inner_memo = {}                  
      console.log("outer cache miss")
      
      outer_memo[arg1] = (arg2) => {
        // just a normal memoized function
        // cache is simple key:value pair
        if (!inner_memo.hasOwnProperty(arg2)) {
          console.log("inner cache miss")
          inner_memo[arg2] = arg1 + arg2
        }
        return inner_memo[arg2]
      }
    }
    return outer_memo[arg1]
  }
  return f
}

let f = getCachedF()
// both caches miss
console.log("3+5", f(3)(5))

// cached result
console.log("3+5", f(3)(5))

// only inside cache hit
console.log("3+8", f(3)(8))

// inside cache only hits if both args are the same
console.log("10+8", f(10)(8))