什么时候GHC Haskell自动记忆?

我无法弄清楚为什么m1显然被记忆,而m2不在以下内容中:

m1      = ((filter odd [1..]) !!)

m2 n    = ((filter odd [1..]) !! n)

m1 10000000在第一次调用时需要大约1.5秒,并且在后续调用中占用一部分时间(可能会缓存列表),而m2 10000000总是花费相同的时间量(每次调用重建列表)。 任何想法发生了什么? 关于GHC是否和何时将记忆功能,有没有什么经验法则? 谢谢。


GHC不会记忆功能。

但是,它确实在每次输入其周围的lambda表达式时至多计算一次代码中的任何给定表达式,或者如果它处于最高级别,则最多只能计算一次。 当你在你的例子中使用语法糖时,确定lambda表达式的位置可能会有点棘手,所以我们将它们转换为等价的desugared语法:

m1' = (!!) (filter odd [1..])              -- NB: See below!
m2' = n -> (!!) (filter odd [1..]) n

(注意:Haskell 98报告实际上描述了一个左操作符部分,比如(a %)等同于b -> (%) ab ,但是GHC将它解除为(%) a 。这些在技术上是不同的,因为它们可以通过seq 。我想我可能已经提交了关于此的GHC Trac票。)

鉴于此,可以看到在m1' ,表达式filter odd [1..]不包含在任何lambda表达式中,所以它只会在程序的每次运行中计算一次,而在m2'filter odd [1..]将在每次输入lambda表达式时计算,即每次调用m2' 。 这解释了你看到的时间差异。


实际上,有些版本的GHC,具有某些优化选项,会分享比上述说明更多的值。 在某些情况下,这可能会有问题。 例如,考虑功能

f = x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])

GHC可能会注意到y不依赖于x并将函数重写为

f = let y = [1..30000000] in x -> foldl' (+) 0 (y ++ [x])

在这种情况下,新版本的效率要低得多,因为它必须从存储y内存中读取大约1 GB,而原始版本将运行在恒定空间并适合处理器的缓存。 实际上,在GHC 6.12.1下,函数f在编译时没有进行优化时的速度几乎是使用-O2编译时的两倍。


m1只计算一次,因为它是一个常量应用表单,而m2不是CAF,因此每次计算都会计算出来。

查看CAF上的GHC wiki:http://www.haskell.org/haskellwiki/Constant_applicative_form


两种形式之间存在着至关重要的区别:单形性限制适用于m1而不适用于m2,因为m2明确给出了参数。 所以m2的类型是一般的,但m1是特定的。 他们分配的类型是:

m1 :: Int -> Integer
m2 :: (Integral a) => Int -> a

大多数Haskell编译器和解释器(我所知的所有这些编译器和实际解释器都不会)记忆多态结构,因此m2的内部列表在每次调用时都会重新创建,其中m1不是。

链接地址: http://www.djcxy.com/p/33313.html

上一篇: When is memoization automatic in GHC Haskell?

下一篇: Git not working on snow leopard