JS:递归函数可以用缓存来装饰吗?

时间:2016-10-24 09:01:58

标签: javascript function caching recursion

我们要制作一个装饰器,缓存确定性函数的计算结果(为了简化,我们假设一个参数函数)。

通常情况下可以这样做:

function makeCacheable(origFunc){
  let registry = {};

  return function (a){
    if (a in registry){
      return registry[a];
    }

    let res = origFunc(a);

    registry[a] = res;

    return res;
  }
}

origFunc递归时出现问题:只有顶级调用通过包装缓存,但其余的递归调用堆栈不符合缓存。无需解释为什么会发生这种情况。我想知道是否有一种自然的方法可以以相同的方式缓存递归函数

function fibonacciF(n) {
  if (n <= 2) return 1;

  let a = 1, b = 1;

  for (let i = 2; i < n; ++i){
    [a, b] = [b, a+b];
  }

  return b;
}

function fibonacciR(n) {
  return n <= 2 ? 1 : (fibonacciR(n-1) + fibonacciR(n-2));
}

let fiboF = makeCacheable(fibonacciF); // OK
let fiboR = makeCacheable(fibonacciR); // actually is not what expected

2 个答案:

答案 0 :(得分:1)

该函数调用名为fibonacciR的函数。如果要通过缓存进行此调用,则必须覆盖fibonacciR

fibonacciR = makeCacheable(fibonacciR);
  

是否有一种自然的方法可以以相同的方式使递归函数可缓存?

没有。通常,不能检查功能实现,并且无论是递归实现还是使用循环或其他方式实现它都不会产生影响。使用纯函数式编程,我们只能将整个函数用作缓存版本(fiboR)的构建块,但我们不能改变其行为或仅采用函数的一部分,除非实现是协作的(例如使用某种可由用户提供的recursion operator

在上面的解决方案中,我们通过覆盖函数内部使用的变量来破坏这些规则,但即使在JavaScript中也不总是这样。

答案 1 :(得分:1)

如果你使用相同的(函数)变量来存储它的装饰版本,你可以使它工作。要允许返回原始属性,可以向属性对象添加属性dependencies { classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.google.gms:google-services:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }

original
function makeCacheable(origFunc){
  let registry = {};
  let f = function (a){
    if (a in registry){
      console.log(`retrieving value from registry[${a}]`);
      return registry[a];
    }
    let res = origFunc(a);
    registry[a] = res;
    return res;
  }
  // Add property for exposing the original function:
  f.original = origFunc;
  return f;
}

function fibonacciR(n) {
  console.log(`Called fibonnacci(${n})`);
  return n <= 2 ? 1 : (fibonacciR(n-1) + fibonacciR(n-2));
}

// Demo illustrating the registry is being used:
console.log('Call fibonnacciR(5) with cache turned on:');
var fibonacciR = makeCacheable(fibonacciR);
var f5 = fibonacciR(5);
console.log(`Result: fibonnaciR(5) = ${f5}`);

// Demo illustrating the function can be restored:
console.log('Call fibonnacciR(5) with cache removed:');
fibonacciR = fibonacciR.original;
f5 = fibonacciR(5);
console.log(`Result: fibonnaciR(5) = ${f5}`);