哈斯克尔:记忆

时间:2016-01-06 23:23:45

标签: haskell recursion memoization

我正在尝试重新学习Haskell,经过多年的努力而忘记了一切,我发现自己仍然困惑于 memoization 。特别是,我正在尝试编写一个程序来生成D[n]n个对象的紊乱数量(在其原始位置没有项目的排列);数字D[n]可以由D[1]=0D[2]=1D[n]=(n-1)(D[n-1]+D[n-2])递归定义。

这样可行:

der :: Int -> Integer
der n = lder !! n
  where lder = 1 : 0 : zipWith3 (\n d1 d2 -> n * (d1+d2)) [1..] lder (tail lder)

就像这样(因为需要三个功能而有点笨拙):

nder :: Int -> Integer
nder n = nderTab !! n

nderTab :: [Integer]
nderTab = [nderCalc n | n <- [0..]]

nderCalc :: Int -> Integer
nderCalc n
  | n == 0    = toInteger 1
  | n == 1    = toInteger 0
  | otherwise = toInteger (n-1) * (nder (n-1) + nder (n-2))

但这不是:

nders :: Int -> Integer
nders n = (map der [0 ..]) !! n
  where der 0 = 1
        der 1 = 0
        der n = (nders (n-2) + nders (n-1)) * toInteger (n-1)

您最后会将此识别为标准记忆斐波纳契数函数的副本。我的函数有效,但没有记忆,因为它挂起大于30的值。另外,如果我写这个函数只对大于或等于1的值进行操作:

nders :: Int -> Integer
nders n = (map der [1 ..]) !! n
  where der 1 = 0
        der 2 = 1
        der n = (nders (n-2) + nders (n-1)) * toInteger (n-1)

根本不起作用。我很想知道这两个函数有什么问题。

1 个答案:

答案 0 :(得分:2)

使用

nders :: Int -> Integer
nders n = (map der [0 ..]) !! n
  where der 0 = 1
        der 1 = 0
        der n = (nders (n-2) + nders (n-1)) * toInteger (n-1)

map der [0..]部分将针对nders的任何应用进行重新计算,尤其包括der中的递归调用。

你可以移出制表的定义,使它(语法上)不依赖于n,这应该做正确的事情:

nders :: Int -> Integer
nders = (memoized !!)
  where 
    memoized = map der [0 ..]

    der 0 = 1
    der 1 = 0
    der n = (nders (n-2) + nders (n-1)) * toInteger (n-1)