将记忆添加到递归定义的 haskell 函数

Add memoization to a recursively defined haskell function

我已经解决了 Haskell 中的动态规划问题(实际上是欧拉计划问题),归结为斐波那契数列的推广:

 f(n) = f(n-1) + f(n-2)
 g(n) = g(n-1) + g(n-3)
 h(n) = h(n-1) + h(n-4)

还有一些这样的功能,由于问题的规模,我不得不添加记忆,这很简单,如下所示:

memF = (map f' [0 ..] !!)
  where f' x | x <=1 = 1
        f' 2         = 2
        f' x         = memF(x-1) + memF(x-2)


memG = (map f' [0 ..] !!)
  where f' x | x <=2 = 1
        f' 3         = 2
        f' x         = memG(x-1) + memG(x-3)

这很好,所以我可以得到 (memF 100) + (memG 100) + ... 的答案并且我已经回答了问题,但是重复的代码很难看,我宁愿定义一个函数来生成记忆函数, 所以像:

mem d = (map f' [0 ..] !!)
  where f' x | x < d  = 1
        f' x | x == d = 2
        f' x          = (mem d) (x-1) + (mem d)(x-d)

然后回答 mem 2 100 + mem 3 100 + ... 这失败了,或者至少缓存不起作用,我猜是因为每次调用都会重新创建数组,我想我可以使用 StateMonad 或记忆库,但我很想知道是否有办法在没有 Monads 的情况下做到这一点。请问有吗?

您需要另一个绑定以避免对 mem d:

的递归调用
mem d = g
  where g = (map f' [0 ..] !!)
        f' x | x < d  = 1
        f' x | x == d = 2
        f' x          = g (x-1) + g (x-d)

调用 mem 时也要小心,因为每次调用 mem 都会创建自己的缓存。例如

mem 10 x + mem 10 y

不会缓存任何东西,而

let g = mem 10 in g x + g y

将使用相同的缓存。

另一种方法是对所有调用 mem d x 使用单个 "global" 缓存,对 (d,x) 对使用记忆。不过,这看起来有点棘手。