Javascript 不变的记忆——可能吗?
我正试图保持我的前端堆栈在功能上尽可能的纯净和不变,它为这个项目创造了奇迹 据说所有可以可变实现的算法都可以不可变地实现,那么在javascript中,我应该如何实现不可变的函数记忆器呢?因为为了通过函数中的不同路径返回,函数内部或外部的一些状态必须改变 使用下面的函数,如何在javascript中实现不可变的记忆Javascript 不变的记忆——可能吗?,javascript,immutability,Javascript,Immutability,我正试图保持我的前端堆栈在功能上尽可能的纯净和不变,它为这个项目创造了奇迹 据说所有可以可变实现的算法都可以不可变地实现,那么在javascript中,我应该如何实现不可变的函数记忆器呢?因为为了通过函数中的不同路径返回,函数内部或外部的一些状态必须改变 使用下面的函数,如何在javascript中实现不可变的记忆 function once(fn){ let returnValue; let canRun = true; return function runOnce(){
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(); //
我对这个问题也很好奇。我同意@zch-传递不可变状态将起作用(初始调用将使用空状态初始化递归) 所以,我做了关于实现斐波那契函数的家庭作业:记住,对于
n-1
和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
为了更好地理解正在发生的事情,cacheResult
和memoize
函数被注入了日志包装器。如您所见,所有函数都是纯函数,只使用一个参数(依赖项注入除外)
请确保,resultcomposition(cache)(result)
-只是取代了{cache,result}
数据结构
另外,我不是Haskell的书呆子(甚至连Haskell或Lisp语法都不知道),但我对函数式编程很感兴趣我提出了一种不同的io方法,使用生成器我们能说它是纯的吗
函数*\u cache(){
常量值=产量为空
而(真实)收益率
}
常量缓存=_缓存()
常量重计算=i=>{
log(`uh-oh这是一些繁重的计算`)
返回i++
}
cache.next()值
cache.next(重计算(1))
cache.next()值
cache.next()值
然后,您可以使用它在运行时缓存值
函数*\u cache(){
常量值=产量为空
而(真实)收益率
}
常量缓存=_缓存()
const loadMongo=uri=>void console.log(`full-loaded${uri}`)
功能结束(loadedMongo){
log('handler called')
}
函数处理程序(){
const mongoUri='Saloos speculos'
const loaded=cache.next().value
如果(已加载===null){
cache.next(loadMongo(mongoUri))
结束(cache.next().value)
}其他端(已加载)
}
handler()
handler()
handler()
handler()
handler()
记忆本质上依赖于状态,无法删除。充其量,您可以通过显式地传递状态使其“纯功能化”。状态将是不变的数据结构,但每次调用后都会有一个不同的数据结构。非常有趣,我将在下班回家后更深入地了解这一点。看起来很可靠,但是我试图避免传递表示“缓存”或“不缓存”的内容,并让函数固有地知道以前是否调用过它。显然,这在某种程度上需要可变性,但如果这种可变性可能存在于我的代码库之外的某个地方(例如javascript引擎本身),那么这是我可以接受的。例如,Redux确实存在状态突变,但根据Redux的思想,所有突变都是不可变的,您应该有一个全局存储。例如,我让我的库实现与Redux接口一致的备忘录(但不是强制性的Redux-您可以编写适配器以符合Redux接口和我的库)该库允许我灵活、模块化,并且仍然具有单一的全局状态。