简体   繁体   English

Haskell / GHC记忆多少钱?

[英]How much does Haskell/GHC memoize?

I wrote the following code to display Pascal's triangle: 我编写了以下代码来显示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

Ignoring the ugliness of ++ [0] 1 , I'm wondering how efficient this code is. 忽略++ [0] 1的丑陋,我想知道这段代码的效率如何。 It seems to me that there are two possibilities. 在我看来,有两种可能性。

In computing pascalRow n after computing all of map pascalRow [1..n-1] : 在计算所有map pascalRow [1..n-1]后计算pascalRow n

  • GHC memoizes the previous values, so previousRow is computed in constant time. GHC会记住以前的值,因此previousRow是在恒定时间内计算的。 (Or maybe O( n ) for the append operation.) Therefore, the calculation of pascalRow n takes only O( n ) time, and constructing all rows up to n (that is, map pascalRow [1..n] ) should take O( n 2 ) time. (或者可能是O( n )用于追加操作。)因此, pascalRow n的计算只需要O( n )时间,并且构造最多n的所有行(即map pascalRow [1..n] )应该采用O( n 2 )时间。
  • GHC forgets the previous values, and so has to recurse all the way down to compute previousRow . GHC会忘记之前的值,因此必须一直递归计算previousRow This seems like it should be O( n 3 ) (because it's Σ i = 0 → n O( n 2 )). 这似乎是它应该是为O(n 3)(因为这是ΣI = 0→N}÷(N 2))。

Which is the case, and how can I improve my implementation? 在哪种情况下,我该如何改进我的实施?


1 though advice here would be appreciated as well! 1虽然这里的建议也会受到赞赏!

You memoize a function by associating it with a data structure that 'remembers' past applications. 通过将函数与“记住”过去的应用程序的数据结构相关联来记忆函数。 Ghc won't remember arbitrary past function applications, but it does remember as much as it has worked out of structure that it is still working on. Ghc将不记得任意过去的函数应用程序,但它确实记住了它仍然在工作的结构。 In this case, the function pascalRow is not really necessary anyway: we just describe the infinite pascal triangle and print as much of it as is needed. 在这种情况下,函数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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM