如何创建记忆功能

问题描述 投票:0回答:6

我被这个记忆问题难住了。我需要创建一个函数,该函数将检查是否已经为给定参数计算了值,返回先前的结果,或者运行计算并返回该值。

我在这上面花了几个小时,而且我还是 JS 新手。我不知道该怎么做。我无法使用任何内置函数,并且真的很想了解我需要做什么。

这是我到目前为止所得到的,这是错误的,在这一点上感觉就像是伪代码。我已经在这里搜索了现有的记忆问题,但我似乎还无法使任何解决方案发挥作用。非常感谢任何帮助。

  myMemoizeFunc = function(passedFunc) {
  var firstRun = passedFunc;
  function check(passedFunc){
    if(firstRun === undefined){
        return passedFunc;
    }else{return firstRun;}
  }
  };

抱歉,我应该说得更清楚。以下是我的具体要求: myMemoizeFunc 必须返回一个函数,该函数将检查是否已经针对给定的 arg 进行了计算,并在可能的情况下返回该 val。 PassedFunc 是保存计算结果的函数。 我知道这可能看起来像重复,但我标记为不是这样,因为我在理解我应该在这里做什么时遇到了一些严重的困难,并且需要比其他帖子中提供的更多帮助。 这就是我的思维过程引导我走向的方向,但我又离目标很远了。

myMemoizeFunc = function(passedFunc) {
var allValues = [];
return function(){
    for(var i = 0; i < myValues.length; i++){
        if(myValues[i] === passedFunc){
            return i;
        }
        else{
            myValues.push(passedFunc);
            return passedFunc;
        }
    }
  }
};

我不应该在这里返回 i 或 passFunc,但是在检查值时我还能在 if/else 中做什么?我已经研究这个问题很长时间了,我开始实现一些荒谬的代码,需要一些新的建议。

javascript underscore.js memoization
6个回答
15
投票

我认为主要技巧是创建一个对象来存储之前作为键传入的参数,并以函数的结果作为值。

为了记忆单个参数的函数,我会像这样实现它:

var myMemoizeFunc = function (passedFunc) {
    var cache = {};
    return function (x) {
        if (x in cache) return cache[x];
        return cache[x] = passedFunc(x);
    };
};

然后您可以使用它来记忆任何采用单个参数的函数,例如用于计算阶乘的递归函数:

var factorial = myMemoizeFunc(function(n) {
    if(n < 2) return 1;
    return n * factorial(n-1);
});

2
投票

将此视为对 Peter Olson 答案的扩展

对于可变数量的参数,您可以使用类似的方法。

注意: 如果您打算传递复杂的参数(数组、对象、函数),则此示例不是最佳选择。请务必进一步阅读,不要盲目复制/粘贴。

function memo(fn) {
  const cache = {};

  function get(args) {
    let node = cache;
    for (const arg of args) {
      if (!("next" in node)) node.next = new Map();
      if (!node.next.has(arg)) node.next.set(arg, {});
      node = node.next.get(arg);
    }
    return node;
  }
  
  return function (...args) {
    const cache = get(args);
    if ("item" in cache) return cache.item;
    cache.item = fn(...args);
    return cache.item;
  }
}

这构建了以下缓存树结构:

const memoizedFn = memo(fn);
memoizedFn();
memoizedFn(1);
memoizedFn(1, 2);
memoizedFn(2, 1);

cache = {
  item: fn(),
  next: Map{ // <- Map contents depicted as object
    1: {
      item: fn(1),
      next: Map{
        2: { item: fn(1, 2) }
      }
    },
    2: {
      next: Map{
        1: { item: fn(2, 1) }
      }
    }
  }
}

此解决方案在传递随后不再引用的复杂参数(数组、对象、函数)时会泄漏内存。

memoizedFn({ a: 1 })

因为

{ a: 1 }
memoizedFn
调用之后没有被引用,所以通常会被垃圾收集。但现在不可能了,因为
cache
仍然保留着引用。它只能被垃圾收集一次
memoizedFn
本身被垃圾收集。

我首先展示了上面的内容,因为它展示了基本概念并以某种简单的形式演示了缓存结构。为了清理通常会被垃圾收集的缓存,我们应该使用

WeakMap
而不是复杂对象的
Map

对于那些不熟悉

WeakMap
的人来说,这些键是一个“弱”参考。这意味着这些键“不”计入对对象的活动引用。一旦一个对象不再被引用(不包括弱引用),它将被垃圾收集。这将从 WeakMap 实例中删除键/值对。

const memo = (function () { const primitives = new Set([ "undefined", "boolean", "number", "bigint", "string", "symbol" ]); function typeOf(item) { const type = typeof item; if (primitives.has(type)) return "primitive"; return item === null ? "primitive" : "complex"; } const map = { "primitive": Map, "complex": WeakMap }; return function (fn) { const cache = {}; function get(args) { let node = cache; for (const arg of args) { const type = typeOf(arg); if (!(type in node)) node[type] = new map[type]; if (!node[type].has(arg)) node[type].set(arg, {}); node = node[type].get(arg); } return node; } return function (...args) { const cache = get(args); if ("item" in cache) return cache.item; cache.item = fn(...args); return cache.item; } } })(); const fib = memo((n) => { console.log("fib called with", n); if (n == 0) return 0; if (n == 1) return 1; return fib(n - 1) + fib(n - 2); }); // heavy operation with complex object const heavyFn = memo((obj) => { console.log("heavyFn called with", obj); // heavy operation return obj.value * 2; }); // multiple complex arguments const map = memo((iterable, mapFn) => { console.log("map called with", iterable, mapFn); const result = []; for (const item of iterable) result.push(mapFn(item)); return result; }); console.log("### simple argument demonstration ###"); console.log("fib(3)", "//=>", fib(3)); console.log("fib(6)", "//=>", fib(6)); console.log("fib(5)", "//=>", fib(5)); console.log("### exlanation of when cache is garbage collected ###"); (function () { const item = { value: 7 }; // item stays in memo cache until it is garbade collected console.log("heavyFn(item)", "//=>", heavyFn(item)); console.log("heavyFn(item)", "//=>", heavyFn(item)); // Does not use the cached item. Although the object has the same contents // it is a different instance, so not considdered the same. console.log("heavyFn({ value: 7 })", "//=>", heavyFn({ value: 7 })); // { value: 7 } is garbade collected (and removed from the memo cache) })(); // item is garbade collected (and removed from memo cache) it is no longer in scope console.log("### multiple complex arguments demonstration ###"); console.log("map([1], n => n * 2)", "//=>", map([1], n => n * 2)); // Does not use cache. Although the array and function have the same contents // they are new instances, so not considdered the same. console.log("map([1], n => n * 2)", "//=>", map([1], n => n * 2)); const ns = [1, 2]; const double = n => n * 2; console.log("map(ns, double)", "//=>", map(ns, double)); // Does use cache, same instances are passed. console.log("map(ns, double)", "//=>", map(ns, double)); // Does use cache, same instances are passed. ns.push(3); console.log("mutated ns", ns); console.log("map(ns, double)", "//=>", map(ns, double));

结构本质上保持不变,但根据参数的类型,它将在

primitive: Map{}

complex: WeakMap{}
对象中查找。
const memoizedFn = memo(fn);
memoizedFn();
memoizedFn(1);
memoizedFn(1, 2);
memoizedFn({ value: 2 }, 1);

cache = {
  item: fn(),
  primitive: Map{
    1: {
      item: fn(1),
      primitive: Map{
        2: { item: fn(1, 2) }
      }
    }
  },
  complex: WeakMap{
    { value: 2 }: { // <- cleared if { value: 2 } is garbage collected
      primitive: Map{
        1: { item: fn({ value: 2 }, 1) }
      }
    }
  }
}

此解决方案不会记住引发的任何错误。基于 

Map 键相等

,参数被视为相等。如果您还需要记住抛出的任何错误,我希望这个答案为您提供了这样做的基础。


0
投票

https://github.com/anywhichway/iMemoized

https://github.com/planttheidea/moize


0
投票
https://stackoverflow.com/a/61402805/2441655


0
投票

const memorized = (fn) => { let cache = {} const makeKey = (...args) => { return args.map((o) => JSON.stringify(o)).join('_') } return function (...args) { const key = makeKey(args) if (cache[key]) { console.log('return from cache') return cache[key] } else { console.log('calculating') const newValue = fn(...args) cache[key] = newValue return newValue } } } const sum = (a, b, c)=> a + b + c const square = a => a*a const sumMemorized = memorized(sum) const squareMemorized = memorized(square) sumMemorized(1,2,3) sumMemorized(1,2,3) sumMemorized(1,2,3) sumMemorized(1,2,3) sumMemorized(1,2,3) squareMemorized(2) squareMemorized(3)



0
投票

const memoize = (func) => { const results = {}; return (...args) => { const argsKey = JSON.stringify(args); if (!results[argsKey]) { results[argsKey] = func(...args); } return results[argsKey]; }; }; const clumsysquare = memoize((num) => { let result = 0; for (let i = 1; i <= num; i++) { for (let j = 1; j <= num; j++) { result++; } } return result; }); console.log(clumsysquare(9467)); console.log(clumsysquare(9467));

console.log(clumsysquare(9467)); console.log(clumsysquare(9467));

使用记忆的重要性和执行时间大大减少

© www.soinside.com 2019 - 2024. All rights reserved.