什么时候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