这是一个小程序,可以打印前 40 个斐波那契数三次 -
module Main where
fib :: Int -> Int
fib 1 = 1
fib 2 = 1
fib n = fib (n-1) + fib (n-2)
main :: IO ()
main = run >> run >> run
where
run = mapM_ (print . fib) [1..40]
如果我用 ghc Main.hs
编译它,那么 GHC 保存了 "run" 的计算部分,因此执行 run
三次与运行一次所需的时间大致相同。这真是个惊喜!我的印象是,一元效应不会像这样自动记忆。
另外,如果我用ghc -O2 Main.hs
编译它,那么记忆就会丢失。(至少在我的机器上使用 ghc 7.10.3)
我如何预测何时要记住某些内容?某处是否有一些记录在案的经验法则?这如何与unsafePerformIO
之类的玩法?
这里run
是一个常量表达式。因此,像任何其他常量表达式一样,它只被计算一次。哦,它编码的 I/O 操作执行三次,但表达式(或者更确切地说,它的子表达式)只计算一次。
我的猜测(这只是一个猜测)是不命名它可能会导致每次都重新评估它。如在
main = do
mapM_ (print . fib) [1..40]
mapM_ (print . fib) [1..40]
mapM_ (print . fib) [1..40]
可能会多次评估它。但我不是100%确定。
现在,如果run
是一个函数:
main = run 40 >> run 40 >> run 40
where
run n = mapM_ (print . fibs) [1..n]
那么它可能会多次评估。我认为 GHC 会避免像这样保留任意函数调用的结果。