“函数记忆”

在计算机领域,记忆(memoization)是主要用于加速程序计算的一种优化技术,它使得函数避免重复盐酸之前已被处理的输入,而返回已缓存的结果。
————摘自维基百科

函数可以将先前操作的结果记录在某个对象里,从而避免无谓的重复运算。JavaScript的对象和数组要实现这种优化是非常方便的,本文将实现一个递归函数,来计算Fibonacci(斐波那契数列)。

下列数组就是一个斐波那契数列,(前面相邻两项之和等于后一项的值)

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

递归实现很简单,代码如下:

var fibonacci = function (n) {
  console.count('fibonacci被执行的次数:')
  return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2)
}

document.body.innerHTML = fibonacci(10)

乍一看,一行代码搞定。但是会有些问题,这里用console.count计算了下fibonacci函数被执行的次数,结果显示:fibonacci被执行的次数: 177

可以看出它做了很多无用功,我们只调用了一次fibonacci函数,它自己却调用了自己176次。

我们可以利用闭包,使我们的函数具备记忆功能。在函数内部声明一个memory数组来储存结果,每次调用时,先判断需要的值是否存在,如果存在直接取就好了。

var fibonacci = function (n) {
  var memory = [0, 1];

  (function run(n) {
    console.count('run执行次数')
    var result = memory[n]

    if (typeof memory[n] !== 'number') {
      result = run(n - 1) + run(n - 2)
      memory[n] = result
      console.count('计算次数')
    }

    return result
  })(n)

  return memory[n]
}

document.body.innerHTML = fibonacci(10)

run执行次数: 19
计算次数: 9

控制台结果显示,计算次数显示只有9次!其余都是直接拿缓存的结果。我们可以多打印些日志,看下函数的入栈出栈顺序,加深理解。

var fibonacci = function (n) {
  var memory = [0, 1];

  (function run(n) {
    console.group('run') // 用group分组查看打印日志,很给力~

    console.log('memory[%d]: ', n, memory[n])
    var result = memory[n]

    if (typeof memory[n] !== 'number') {
      result = run(n - 1) + run(n - 2)
      memory[n] = result
      console.log(`%c计算结果 ${result}`, 'color: red;')
      console.count('计算次数')
    } else {
      console.log('直接拿到结果', result)
    }

    console.log('memory', memory)
    console.groupEnd()
    return result
  })(n)

  return memory[n]
}

打印结果:


你可能感兴趣的:(“函数记忆”)