haskell如何存档记忆

时间:2017-02-08 02:17:34

标签: haskell dynamic-programming

我正在尝试从Project Euler解决#15,这是我的第一个解决方案     import Data.List

type Location = (Int,Int)

boardX = 20
boardY = 20

stepBack :: Location -> [Location]
stepBack (x,y) = [(x-1,y), (x,y-1)]

legalStep :: Location -> Bool
legalStep (x,y) = x >= 0 && y >= 0

iteractStep :: Int -> [[Location]]
iteractStep 0 = [[(boardX, boardY)]]
iteractStep n = [x:y|y <- iteractStep (n-1), x<- stepBack (head y),       legalStep x]

main :: IO ()
main = putStrLn $ show $ length $ iteractStep (boardX + boardY)

这很慢,我发现有很多子问题重新计算,我试着记住子问题的答案,然后我找到了这个例子:

  memoized_fib :: Int -> Integer
  memoized_fib = (map fib [0 ..] !!)
    where fib 0 = 0
          fib 1 = 1
          fib n = memoized_fib (n-2) + memoized_fib (n-1)

我在ghci中尝试它,我发现它实际上记住了子问题的结果,我无法理解这段代码如何记住结果,它看起来像

map fib [0 ..] 

是全局的,但我认为它是方法范围(仅存在于每个方法调用中),为什么它可以记住结果?

1 个答案:

答案 0 :(得分:2)

memoized_fib有效,因为它最初只是在thunk中创建map fib [0..](这是懒惰,需要呼叫)。在后续调用中,计算列表中的fib值越来越多,同时每个调用通过索引到列表中来查找fib值(!!)。

如果没有列表,则每次调用fib都会再次调用fib两次,这会再次进行两次调用,等等。在算法术语中,这会从{O}更改fib 2)到O(n)。

如果没有查看Project Euler问题,我建议您首先尝试使用其他数据结构。在您的情况下,您应该尝试使用vectorarray。这些都比你当前使用的列表快得多,实际上是一个链表。

您可能还会将您的电路板表示更改为board :: [Int]。我们直观地认为电路板有两个维度,但它很容易实现为列表而不是列表列表。 (我记得PAIP

中的一章

对于其他问题,请牢记containersunordered-containers