Запись _.Memoize из нижней панели в Javascript

#javascript

#javascript

Вопрос:

В настоящее время я изучаю Javascript и занимаюсь проектом underbar (переписываю библиотеку _underbar).

Я решил все из них, кроме одного, так как я застрял на _.memoize

Это мой текущий код

 _.memoize = function(func) {
    var cache = {};
    var slice = Array.prototype.slice;

    return function() {
      var args = slice.call(arguments);
      if (args in cache) {
        return cache[args];
      } else {
        return (cache[args] = func.apply(this, args));
      }
    }
  };
  

Это тестовый пример, который я не могу пройти

 // Here, we wrap a dummy function in a spy. A spy is a wrapper function (much like _.memoize
// or _.once) that keeps track of interesting information about the function it's spying on;
// e.g. whether or not the function has been called.

it('should run the memoized function twice when given an array and then given a list of arguments', function() {
        // Be careful how you are checking if a set of arguments has been passed in already
        var spy = sinon.spy(function() { return 'Dummy output'; });
        var memoSpy = _.memoize(spy);

        memoSpy([1, 2, 3]);
        expect(spy).to.have.been.calledOnce;
        memoSpy(1, 2, 3);
        expect(spy).to.have.been.calledTwice;
      });
  

Или в словах «Он должен дважды запускать записанную функцию при задании массива, а затем списка аргументов»

Я попытался изменить аргументы, проверив, был ли первый массив массивом, и если не все аргументы массивом, но безуспешно. Я также попытался переписать код для хранения результата и использовать Фибоначчи для хранения кэша, но это привело бы к двойному запуску функции.

Как бы я разрешил этот тестовый пример?

Спасибо

Комментарии:

1. Подсказка: memoSpy("1,2,3"); . Что именно args in cache делает?

2. другой намек: arguments это уже массив.

Ответ №1:

Использование cache[args] не имеет особого смысла, потому args что это массив, а использование скобок преобразует его в строку. Вы могли бы создать массив массивов аргументов с их возвращаемыми значениями, а затем сравнить массивы:

 const _ = {};
_.memoize = function(func) {
  const allArgs = [];
  return function(...args) {
    // Try to find matching arguments in allArgs
    const result = allArgs.find(
      item => item.args.length === args.length amp;amp; item.args.every(
        (arg, i) => args[i] === arg
      )
    );
    // If a matching argument array was found, return the result:
    if (result) return result.returnValue;
    const returnValue = func.apply(this, args);
    allArgs.push({ args, returnValue });
    return returnValue;
  }
};

const memoizedSum = _.memoize((...args) => {
  console.log('called with', args);
  return args.reduce((a, b) => a   b, 0);
});

console.log(memoizedSum(1, 2, 3));
console.log(memoizedSum(1, 2, 3));
console.log(memoizedSum(4, 5, 6));  

Другим вариантом с меньшей вычислительной сложностью было бы создание Map для каждого аргумента. Например, вызов с аргументами 1, 2, 3 может привести к структуре данных:

 Map([[
  1, { nested: Map([[
    2, { nested: Map([[
      3, { result: 6 }
    ]])}
  ]])}
]])
  

Затем вы обнаружите, существует ли вложенный результат для заданных аргументов, рекурсивно перебирая карты .get для каждого аргумента, вместо перебора всех аргументов, с которыми функция была вызвана до сих пор. Код был бы сложным, но он был бы более эффективным.

Комментарии:

1. Я предполагаю, что упражнение нацелено на простой JSON.stringifiy

2. Если аргументы гарантированно для всех всегда будут сериализуемыми, конечно, но OP этого не сказал, и это не кажется безопасным предположением — оно не будет точно реализовано _.memoize . Версия подчеркивания принимает и правильно кэширует несериализуемые аргументы

Ответ №2:

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

 cache[args] = func.apply(this, args);

return cache[args];