在像Haskell这样的函数式语言中,memoized值的生命周期是多少?

时间:2011-05-05 13:05:19

标签: haskell functional-programming memoization

在具有惰性语义的纯函数式语言(例如Haskell)中,计算结果被记忆,因此对具有相同输入的函数的进一步求值不会重新计算该值,而是直接从memoized值的缓存中获取它。 / p>

我想知道这些记忆值是否会在某个时间点被回收?

  1. 如果是这样,这意味着必须在以后重新计算已记忆的值,并且记忆的好处并非如此,恕我直言。
  2. 如果没有,那么好吧,这很容易缓存所有内容......但这是否意味着一个程序 - 如果运行足够长的时间 - 将 总是消耗越来越多的内存?
  3. 想象一个程序执行密集的数值分析:例如,使用二分法算法找到数十万个数学函数列表的根。

    每次程序评估具有特定实数的数学函数时,结果都将被记忆。但是概率很小 算法期间会再次出现完全相同的实数,导致内存泄漏(或者至少是非常糟糕的使用情况)。

    我的想法是,或许memoized值只是简单地“限定”程序中的某些东西(例如当前的继续,调用堆栈等),但我无法找到关于该主题的实际内容。

    我承认我没有深入研究Haskell编译器实现(懒惰?),但是,有人可以向我解释它在实践中是如何工作的吗?


    编辑:好的,我从最初的几个答案中理解了我的错误:纯语义意味着参考透明度,这反过来并不意味着自动记忆,但只是保证它没有问题。

    我认为网上的一些文章误导了这一点,因为从初学者的角度来看,参考透明度属性似乎很酷,因为它允许隐式记忆。

2 个答案:

答案 0 :(得分:19)

Haskell 自动记忆函数调用,正是因为这会快速消耗大量内存。如果你自己做了memoziation,你可以选择函数被记忆的范围。例如,假设你有像这样定义的斐波纳契函数:

fib n = fibs !! n
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

此处的备忘录只能在一次致fib的情况下完成,而如果您将fibs留在最高级别

fib n = fibs !! n

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

然后保留记忆列表,直到垃圾收集器可以确定没有更多方法从程序的任何部分到达fibs

答案 1 :(得分:5)

  

我的想法是,或许memoized值只是简单地“限定”程序中的某些东西(例如当前的延续,调用堆栈等),但我无法找到关于该主题的实际内容。

这是对的。具体来说,当你看到类似的东西时:

fun x y = let
    a = .....
    b = ....
    c = ....
in ....

或等效的where子句,在实际使用之前可能不会计算值a,b和c(或者可以立即计算它们,因为严格性分析器可以证明以后无论如何都要评估这些值)。但是当这些值依赖于当前函数参数(此处为x和y)时,运行时很可能不会记住x和y的每个组合以及结果a,b和c。

另请注意,在纯语言中,根本不记得这些值也是可以的 - 只有在内存比CPU时间便宜的情况下,这才是实用的。

所以问题的答案是:在Haskell中没有中间结果的生命周期。所有人都可以说,评估值将在需要时存在。