不可变记忆 - 有可能吗?

Sha*_*son 4 javascript immutability

我试图让我的前端堆栈在功能上尽可能纯粹和不可变,并且它为这个项目创造了奇迹。

据说所有可以可变实现的算法都可以不可变地实现,那么在javascript中我将如何实现一个不可变的函数记忆器?因为为了通过函数中的不同路径返回,函数内部或外部的某些状态必须改变。

使用以下函数,如何在 javascript 中拥有不可变的记忆?

function once(fn){
  let returnValue;
  let canRun = true;
  return function runOnce(){
      if(canRun) {
          returnValue = fn.apply(this, arguments);
          canRun = false;
      }
      return returnValue;
  }
}
var processonce = once(process);
processonce(); //process
processonce(); //
Run Code Online (Sandbox Code Playgroud)

Bla*_*ula 6

我也很好奇这个问题。我同意@zch - 传递不可变状态会起作用(初始调用将用空状态初始化递归)。

所以,我完成了实现斐波那契函数的功课:记住,我们至少需要两次 forn-1和 for n-2。当我们打电话时fib(n-2)- 它已经被记住了。

这是我的实现:

const resultCombination = cache => result => (getResult = true) => getResult ? result : cache
const cacheResult = ({resultCombination}) => result => n => resultCombination({...result(false), [n]: result()})(result())

const memoize = ({resultCombination, cacheResult}) => cache => f => n => cache.hasOwnProperty(n)
    ? resultCombination(cache)(cache[n])
    : cacheResult({resultCombination})(f(cache)(n))(n)

const fib2 = ({resultCombination, cacheResult, memoize}) => f1Result => f => n => resultCombination(f1Result(false))(f1Result() + memoize({resultCombination, cacheResult})(f1Result(false))(f)(n - 2)())

const fib = ({resultCombination, cacheResult, memoize, fib2}) => cache => n => n === 1 || n === 2
    ? resultCombination(cache)(1)
    : fib2({resultCombination, cacheResult, memoize})(memoize({resultCombination, cacheResult})(cache)(fib({resultCombination, cacheResult, memoize, fib2}))(n - 1))(fib({resultCombination, cacheResult, memoize, fib2}))(n)


console.log('THE RESULT: ' + fib({
    resultCombination,
    cacheResult: ({resultCombination}) => result => n => {
        console.log(`Caching result: f(${n})=${result()}`)
        return cacheResult({resultCombination})(result)(n)
    },
    memoize: ({resultCombination, cacheResult}) => cache => f => n => {
        console.log(cache.hasOwnProperty(n) ? `Cache hit for n=${n}` : `Calculating value for f(${n})`)
        return memoize({resultCombination, cacheResult})(cache)(f)(n)
    },
    fib2
})({})(8)(true))

// Calculating value for f(7)
// Calculating value for f(6)
// Calculating value for f(5)
// Calculating value for f(4)
// Calculating value for f(3)
// Calculating value for f(2)
// Caching result: f(2)=1
// Calculating value for f(1)
// Caching result: f(1)=1
// Caching result: f(3)=2
// Cache hit for n=2
// Caching result: f(4)=3
// Cache hit for n=3
// Caching result: f(5)=5
// Cache hit for n=4
// Caching result: f(6)=8
// Cache hit for n=5
// Caching result: f(7)=13
// Cache hit for n=6
// THE RESULT: 21
Run Code Online (Sandbox Code Playgroud)

为了更好地理解,发生了什么 -cacheResultmemoize函数被注入了日志包装器。可以看到,所有函数都是纯函数,只带一个参数(依赖注入除外)。

可以肯定,那resultCombination(cache)(result)- 只是{cache, result}数据结构的替换。

PS 我不是 Haskell 书呆子(甚至根本不知道 Haskell 或 Lisp 语法),但我对函数式编程充满热情