【问题标题】:JS: Can a recursive function be decorated with cache?JS:可以用缓存修饰递归函数吗?
【发布时间】:2016-10-24 09:01:58
【问题描述】:

我们将创建一个装饰器,缓存确定性函数的计算结果(为简单起见,我们假设一个参数函数)。

一般情况下可以这样做:

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

【问题讨论】:

    标签: javascript function caching recursion


    【解决方案1】:

    如果你使用相同的(函数)变量来存储它的修饰版本,你可以让它工作。要允许返回到原始状态,您可以在函数对象中添加属性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}`);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

      【解决方案2】:

      该函数调用名为fibonacciR 的函数。如果你想让这个调用通过缓存,你必须覆盖fibonacciR

      fibonacciR = makeCacheable(fibonacciR);
      

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

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

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

      【讨论】:

        猜你喜欢
        • 2020-05-20
        • 2010-09-16
        • 2011-03-05
        • 1970-01-01
        • 1970-01-01
        • 2013-01-10
        • 2014-09-10
        • 2022-01-01
        相关资源
        最近更新 更多