12

一个初学者的问题。我对以下内容有点困惑:我读到 GHC(Haskell 的?)惰性评估方法包括使用共享,例如评估表达式

(1+1)*(1+1)

只会计算1+1一次。在 Graham Hutton 所著的“用 Haskell 编程”一书中,解释说这是通过用1+1指向它的一个副本的指针替换出现的两个并仅评估那个副本来实现的。

但是计算第 n 个斐波那契数fib n = if (n<2) then n else fib (n-1) + fib(n-2)需要 n 中的指数时间。我对上述内容的理解告诉我,例如fib 5将被这样评估:

fib 5 => fib 4 + fib 3 => fib 3 + fib 2 + fib 3 => x + fib 2 + x

x = fib 3 = fib 2 + fib 1 = y + fib 1

所以fib 5 = y + fib 1 + y + y + fib 1

y = fib 2 = fib 1 + fib 0 = 1

所以fib 5 = 1 + 1 + 1 + 1 + 1

其中xy被作为共享值引入。

fib k但是这种处理方式虽然比迭代生成for的标准方式效率稍低,但2<=k<=n显然不会像评估函数那样导致运行时间如此之长。那么我的理解有什么问题呢?

4

1 回答 1

20

您不能依赖 GHC 中的公共子表达式消除(出于技术原因,因为共享惰性值会引入空间泄漏)。

一个好的经验法则是值是按名称共享的。所以命名你的子表达式,如下所示:

 n * n
    where n = 1 + 1

你保证分享。

请注意,在您的简单示例中,GHC 只会在编译时计算整个事情。上面的讨论实际上只适用于“大”数据。

您可以使用诸如 Vacuum之类的调试工具来观察共享。此类共享值表示为具有多个引用的堆分配对象:

在此处输入图像描述

于 2012-11-14T16:45:37.647 回答