在像Haskell这样的函数式语言中,记忆值的生命周期是什么?



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

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

    如果是这样,这意味着记忆值必须在以后的时间重新计算,并且记忆的好处并不那么明显。
  1. 如果没有,那么好吧,这是聪明的缓存一切…但这是否意味着一个程序——如果运行足够长的一段时间——会总是消耗越来越多的内存?

想象一个执行密集数值分析的程序:例如,使用二分算法查找包含数十万个数学函数的列表的根。

每当程序用一个特定的实数计算一个数学函数时,结果将被记忆。但这种可能性很小在算法过程中会再次出现完全相同的实数,导致内存泄漏(或者至少,非常糟糕的使用)。

我的想法是,也许记忆值只是"作用域"到程序中的某些东西(例如到当前的延续,调用堆栈等),但我无法在这个问题上找到一些实用的东西。

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


编辑:好吧,我从前几个答案中理解了我的错误:纯语义意味着引用透明度,这反过来并不意味着自动记忆,而只是保证不会有问题。

我认为网络上的一些文章对此有误导,因为从初学者的角度来看,Referential Transparency属性似乎很酷,因为它允许隐式记忆。

Haskell没有自动记忆函数调用,正是因为这会很快消耗大量内存。如果你自己记忆,你可以选择在什么范围内记忆函数。例如,假设您有fibonacci函数定义如下:

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

我的想法是,也许记忆值只是简单地"作用域"到程序中的某些内容(例如当前的延续,调用堆栈等),但我无法找到有关该主题的实用内容。

这是正确的。具体来说,当您看到如下内容时:

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

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

还请注意,在纯语言中,完全不记住值也是可以的——这只有在内存比CPU时间便宜的情况下才实用。

所以你的问题的答案是:在Haskell中没有中间结果的生命周期。我们所能说的就是,求出的值会在需要的时候出现。

最新更新