Haskell / GHC记忆多少钱?

时间:2014-12-19 16:47:39

标签: performance haskell functional-programming ghc memoization

我编写了以下代码来显示Pascal的三角形:

import Control.Monad
import Data.List

pascalRow :: Integer -> [Integer]
pascalRow 0 = [1]
pascalRow n = map sumParents pairs
  where previousRow = 0:(pascalRow $ n - 1)++[0]
        pairs = zip previousRow (tail previousRow)
        sumParents (a, b) = a + b

-- Read an integer from stdin, and print the Pascal triangle of that height.
main = do
  n <- readLn
  forM_ [0..n-1] printRow
    where printRow k = putStrLn $ intercalate " " $ map show $ pascalRow k

忽略++ [0] 1 的丑陋,我想知道这段代码的效率如何。在我看来,有两种可能性。

在计算所有pascalRow n之后计算map pascalRow [1..n-1]

  • GHC 记忆以前的值,因此previousRow是在恒定时间内计算的。 (或者可能是O( n )用于追加操作。)因此,pascalRow n的计算只需要O( n )时间,并构建所有行到 n (即map pascalRow [1..n])应该花费O( n 2 )时间。
  • GHC 忘记以前的值,因此必须一直递减到计算previousRow。这似乎应该是O( n 3 )(因为它是Σ i = 0→ n O( n 2 ))。

情况如何,我该如何改进实施?


1 虽然这里的建议也会受到赞赏!

1 个答案:

答案 0 :(得分:9)

通过将函数与“记住”过去的应用程序的数据结构相关联来记忆该函数。 Ghc将不记得任意过去的函数应用程序,但它确实记住了它仍然在工作的结构。在这种情况下,函数pascalRow无论如何都不是必需的:我们只是描述无限的pascal三角形并根据需要打印它。

import Control.Monad
import Data.List

pstep :: [Integer] -> [Integer]
pstep xs = zipWith (+) (0:xs) (xs ++ [0])

-- the infinite pascal triangle
pascal = iterate pstep [1] 
pascalRow n = pascal !! n  -- not needed, but fine

-- Read an integer from stdin, 
-- and print that much of the infinite Pascal triangle.
main = do
      n <- readLn
      mapM_ printRow (take n pascal)
  where printRow xs = putStrLn $ intercalate " " $ map show xs