我的“记忆式” pascal功能无法正常工作

问题描述

import Data.List (intercalate)
import Control.Concurrent (threadDelay)
import Data.Maybe (fromJust)
import System.IO


-- I love how amazingly concise Haskell code can be.  This same program in C,C++ or Java
-- would be at least twice as long.


pascal :: Int -> Int -> Int
pascal row col | col >= 0 && col <= row =
                 if row == 0 || col == 0 || row == col
                 then 1
                 else pascal (row - 1) (col - 1) + pascal (row - 1) col
pascal _ _ = 0


pascalFast' :: [((Int,Int),Int)] -> Int -> Int -> Int
pascalFast' dict row col | col > row = 0
pascalFast' dict row col | row == 0 || col == 0 || row == col = 1
pascalFast' dict row col =
  let value1 = lookup (row - 1,col - 1) dict
      value2 = lookup (row - 1,col) dict
  in if not(value1 == nothing || value2 == nothing)
     then (fromJust value1) + (fromJust value2)
     else let dict'  = ((row - 1,col),pascalFast' dict (row - 1) col) : dict
              dict'' = ((row - 1,col - 1),pascalFast' dict' (row - 1) (col - 1)) : dict' 
          in (pascalFast' dict'' (row - 1) col) + (pascalFast' dict'' (row - 1) (col - 1))


pascalFast = pascalFast' []
                

pascalsTriangle :: Int -> [[Int]]
pascalsTriangle rows =
  [[pascal row col | col <- [0..row]] | row <- [0..rows]]


main :: IO ()
main = do
  putStrLn "" 
  putStr "Starting at row #0,how many rows of Pascal's Triangle do you want to print out? "
  hFlush stdout
  numRows <- (\s -> read s :: Int) <$> getLine
  let triangle = pascalsTriangle numRows
      longestStringLength = (length . show) $ foldl1 max $ flatten triangle
      triangleOfStrings = map (intercalate ",") $ map (map (pad longestStringLength)) triangle
      lengthOfLastDiv2 = div ((length . last) triangleOfStrings) 2 
  putStrLn ""
  mapM_ (\s -> let spaces = [' ' | x <- [1 .. lengthOfLastDiv2 - div (length s) 2]]
                   in (putStrLn $ spaces ++ s) >> threadDelay 200000) triangleOfStrings
  putStrLn ""
  

flatten :: [[a]] -> [a]
flatten xs =
  [xss | ys <- xs,xss <- ys]


pad :: Int -> Int -> String
pad i k =
  [' ' | _ <- [1..n]] ++ m
  where m = show k
        n = i - length m

对于我的一生,我不明白为什么pascalFast不是FAST !!!它进行类型检查并在数学上是正确的,但是我的“ pascalFast”功能与我的“ pascal”功能一样慢。有任何想法吗?不,这不是家庭作业。我只是想自己尝试一下。感谢您的反馈。

最好, 道格拉斯·莱威特

解决方法

您的main实际上根本没有调用pascalFast,因此我不清楚您在做什么,这导致您断定它很慢-尽力而为,我可以告诉它观察起来很慢,但是问题中的一些证据会很好。

为什么,我遇到了两个问题。在我看来,由于您将字典“向上”传递给基本情况,而从不向下或向侧面传递,因此您只是在缓存结果,您将永远不会再看。尝试在纸上手工评估pascalFast [] 2 1,看看是否遇到过缓存问题。

第二,即使您正确地缓存了,使用lookup的时间也会与列表大小成线性关系,因此您的运行时至少要生成二次项:对于您生成的每个项目,您都会至少一次查看所有其他项目。为了有效地缓存,您需要一个真实的数据结构,例如Data.Map中的结构。

但是,除了如何有效记忆的问题之外,通常最好根本不记忆,通过从基础案例开始并逐步积累,而不是从最终结果中得出。对于Pascal的Triangle,类似这样的东西非常经典:

triangle :: [[Int]]
triangle = iterate nextRow [1]
  where nextRow xs = 1 : zipWith (+) xs (tail xs) ++ [1]

main :: IO ()
main = print $ take 5 triangle