Answers:
GHC不会记住功能。
但是,它确实会在每次输入其周围的lambda表达式时最多计算一次代码中的任何给定表达式,或者如果在顶级时则最多计算一次。当像示例中那样使用语法糖时,确定lambda表达式的位置可能会有些棘手,因此让我们将其转换为等效的已删除语法:
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(注意:Haskell 98报告实际上描述了一个左操作符部分(a %),等同于\b -> (%) a b,但是GHC将其删除(%) a。这在技术上是不同的,因为它们可以被区分seq。我想我可能已经为此提交了GHC Trac票证。)
鉴于此,您可以看到in m1'中的表达式filter odd [1..]不包含在任何lambda-expression中,因此,该表达式每次程序运行仅计算一次,而in中m2',filter odd [1..]则将在每次输入lambda-expression时计算一次,即,在的每次通话中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])
在这种情况下,新版本的效率要低得多,因为它必须从存储的内存中读取大约1 GB y,而原始版本将在恒定的空间中运行并适合处理器的缓存。实际上,根据GHC 6.12.1,在不进行优化的情况下f编译该函数的速度几乎是使用编译时的两倍。-O2
f:main = interact $ unlines . (show . map f . read) . lines; 有无编译-O2; 然后echo 1 | ./main。如果编写类似的测试main = print (f 5),则y可以在使用时对其进行垃圾回收,并且两者之间没有区别f。
map (show . f . read)当然应该是。现在,我已经下载了GHC 6.12.3,看到的结果与GHC 6.12.1中的结果相同。是的,您对GHC 的原始m1和m2版本是正确的:在启用了优化的情况下执行这种提升的GHC版本将转换m2为m1。
m1仅计算一次,因为它是一个常数适用形式,而m2不是CAF,因此每次评估都要计算一次。
请参阅CAF上的GHC Wiki:http://www.haskell.org/haskellwiki/Constant_applicative_form
[1 ..]是在程序执行期间仅计算一次,还是针对该功能的每个应用程序计算一次,但是与CAF相关吗?
m1是CAF,因此第二个适用,并且filter odd [1..](不只是[1..]!)仅计算一次。GHC还可能注意到m2指向filter odd [1..],并放置一个与相同的thunk的链接m1,但这不是一个好主意:在某些情况下,它可能导致大量内存泄漏。
[1..]和filter odd [1..]。对于其余的,我仍然不服气。如果我没记错的话,那么CAF仅在我们要争辩说编译器可以用全局thunk 替换filter odd [1..]in 时才有意义m2(该thunk 可能与in中使用的thunk相同m1)。但在提问者的情况,编译器并没有这样做“优化”,而我不能看到其相关的问题。
m1,它也。
两种形式之间有一个关键的区别:单态性限制适用于m1而不适用于m2,因为m2明确给出了参数。因此,m2的类型是常规的,而m1的类型是特定的。为其分配的类型为:
m1 :: Int -> Integer
m2 :: (Integral a) => Int -> a
大多数Haskell编译器和解释器(我实际上都知道所有这些)并不记忆多态结构,因此,每次调用m2时都会重新创建m2的内部列表,而不会调用m1的内部列表。
我不确定,因为我本人对Haskell还是陌生的,但似乎是因为第二个函数已参数化,而第一个没有参数化。函数的性质是,其结果取决于输入值,而在功能范例中,它仅取决于输入。明显的含义是,无参数的函数总是反复地返回相同的值,无论如何。
显然,GHC编译器中有一个优化机制,可以利用这一事实在整个程序运行时仅计算一次该函数的值。可以肯定的是,它确实很懒惰,但是仍然可以做到。当我编写以下函数时,我自己注意到了它:
primes = filter isPrime [2..]
where isPrime n = null [factor | factor <- [2..n-1], factor `divides` n]
where f `divides` n = (n `mod` f) == 0
然后进行测试,我进入GHCI并写道:primes !! 1000。花费了几秒钟,但最终我得到了答案:7927。然后我打电话primes !! 1001给我,并立即得到答案。类似地,在瞬间我得到了的结果take 1000 primes,因为Haskell必须计算整个千元素列表才能返回前1001个元素。
因此,如果您可以编写不带任何参数的函数,则可能需要它。;)
seqm1 10000000)。但是,如果未指定优化标志,则有所不同。顺便提一下,“ f”的两个变体的最大驻留时间均为5356字节,而与优化无关(使用-O2时总分配较少)。