【问题标题】:Add memoization to a recursively defined haskell function将记忆添加到递归定义的 haskell 函数
【发布时间】:2019-11-29 09:56:25
【问题描述】:

我已经解决了 Haskell 中的一个动态规划问题(实际上是一个 Project Euler 问题),归结为斐波那契数列的概括:

 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 或 memoization 库,但我会有兴趣知道是否有办法在没有 Monads 的情况下做到这一点。请问有吗?

【问题讨论】:

    标签: haskell recursion memoization


    【解决方案1】:

    您需要另一个绑定来避免递归调用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 使用单个“全局”缓存,对(d,x) 使用memoization。不过,这看起来有点棘手。

    【讨论】:

    • 好的,谢谢:所以绑定捕获了函数调用和增量值。不错。
    • @user118165 是的,就是这样。
    • 全局缓存是一个有趣的想法——我猜你会使用地图?当然,正如您指出的那样,在这种情况下它并没有增加太多。再次感谢。
    • @user118165 我不确定我现在会使用什么。 Data.Map.Map 是严格的,所以不能使用。我们需要一个惰性数据结构。
    猜你喜欢
    • 2022-12-03
    • 2018-09-08
    • 2019-08-02
    • 2022-01-04
    • 2012-11-26
    • 1970-01-01
    • 1970-01-01
    • 2014-10-31
    • 2012-04-01
    相关资源
    最近更新 更多