记忆是一个有用的东西,因为它与函数密切相关,我假设Haskell有一个正确的机制来以至少相当简单的方式实现它。
var memo = function (f) {
var cache = {};
return function (x) {
if (cache[x]) return cache[x];
else return cache[x] = f(x);
}
}
//Usage:
var fib = memo(function (x) {
if (x == 0) return 1;
if (x == 1) return 1;
return fib(x - 1) + fib(x - 2);
});
fib(100);
这是我在JavaScript中编写的代码,它可以实现我想要的功能。对Haskell有什么好的翻译可以提供类似的实用性和性能?
为了减少问题的模糊性,我对复制JS解决方案的一般性不感兴趣,因为Haskell是强类型的。类型签名为
的东西memo :: (Int -> b) -> (Int -> b)
可以为多个参数手动扩展,甚至可能是各种类型。
答案 0 :(得分:9)
JavaScript解决方案最重要的是快速访问可变容器;这些语言通常将它们作为哈希映射实现,并使它们成为一个中心语言组件(主要是因为更复杂,专业的数据结构无法在微弱的类型系统中表达)。
但不是在Haskell。库中有hash-maps可以,但还有专为记忆设计的容器 :memo-tries。为什么不使用那些?
当然,如果没有高效的容器结构,你也可能会破解你的方式。记住Int ->
函数的最简单方法是存储所有结果的无限列表:
memoInt :: (Int -> b) -> Int -> b
memoInt f = look
where positives = [ f x | x <- [0..] ]
negatives = [ f x | x <- [-1, -2 ..] ]
look x | x<0 = negatives !! (1-x)
| otherwise = positives !! x
显然,对于大|x|
列表查找变得非常昂贵。
对于一种有点奇怪,但非常简单的方法来使这个相当快,即 O (√ n )而不是 O (< EM>名词的):
memoInt' :: (Int -> b) -> Int -> b
memoInt' f = look
where table = [ [ f (sqrtX^2 + dx) | dx <- [0..] ]
| sqrtX <- [0..] ]
look x | x<0 = negDomain x
| otherwise = let sqrtX = floor . sqrt $ fromIntegral x
in table !! sqrtX !! (max 0 $ x - sqrtX)
negDomain = memoInt' (f . negate)
(由于浮点问题,这可能会因大量数据而中断,sqrt
并非如此安全使用