#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];