在Haskell

时间:2015-12-23 17:08:10

标签: haskell memoization

我有一个递归函数f,它有两个参数xy。该函数由第一个参数唯一确定;第二个只是让事情变得更容易。

我现在想要记住那个函数w.r.t.它是第一个参数而忽略了第二个参数。 (即fx}

的每个值最多评估f

最简单的方法是什么?目前,我只是简单地定义一个包含递归的所有值的数组,但这是一个有点临时的解决方案。我更喜欢某种记忆组合,我可以把它放在我的功能上。

编辑:澄清一下,函数xs采用一对整数和一个列表。第一个整数是一些参数值,第二个整数表示要使用的某个全局列表f中的元素的索引。

为了避免为列表编制索引,我也将部分使用的列表传递给(m, n),但显然,不变量是如果第一个参数是drop n xs,则第二个参数将始终是{{} 1}},因此结果由第一个参数唯一确定。

在部分应用的函数上使用memoisation组合器将不起作用,因为这将留下未评估的thunk \xs -> …。我可以将这两个参数包装在一个数据类型中,其Eq实例忽略第二个值(对于其他实例也是如此),但这似乎是一个非常临时的解决方案。有没有更简单的方法?

EDIT2:我要记忆的具体功能:

g :: [(Int, Int)] -> Int -> Int
g xs n = f 0 n
  where f :: Int -> Int -> Int
        f _ 0 = 0
        f m n
            | m == length xs  = 0
            | w > n           = f (m + 1) n
            | otherwise       = maximum [f (m + 1) n, v + f (m + 1) (n - w)]
          where (w, v) = xs !! m

为了避免昂贵的索引操作,我将部分使用的列表传递给f

g' :: [(Int, Int)] -> Int -> Int
g' xs n = f xs 0 n
  where f :: [(Int, Int)] -> Int -> Int -> Int
        f []           _ _ = 0
        f _            _ 0 = 0
        f ((w,v) : xs) m n
            | w > n           = f xs (m + 1) n
            | otherwise       = maximum [f xs (m + 1) n, v + f xs (m + 1) (n - w)]

f w.r.t的记事列表参数当然是不必要的,因为列表不会(道德地)影响结果。因此,我希望memoisation简单地忽略list参数。

1 个答案:

答案 0 :(得分:2)

您的功能不必要地复杂化。您根本不需要索引m

foo :: [(Int, Int)] -> Int -> Int
foo []         _ = 0
foo _          0 = 0
foo ((w,v):xs) n
    | w > n      = foo xs n
    | otherwise  = foo xs n `max` foo xs (n - w) + v

现在,如果你想记住foo,那么必须考虑两个参数(应该是这样)。

我们将使用monadic memoization mixin方法记住foo

  1. 首先,我们创建一个未经验证的foo版本(因为我们想要记住两个参数):

    foo' :: ([(Int, Int)], Int) -> Int
    foo' ([],       _) = 0
    foo' (_,        0) = 0
    foo' ((w,v):xs, n)
        | w > n       = foo' (xs, n)
        | otherwise   = foo' (xs, n) `max` foo' (xs, n - w) + v
    
  2. 接下来,我们对函数foo'进行monadify(因为我们想在函数中创建一个memo表):

    foo' :: Monad m => ([(Int, Int)], Int) -> m Int
    foo' ([],       _) = return 0
    foo' (_,        0) = return 0
    foo' ((w,v):xs, n)
        | w > n        = foo' (xs, n)
        | otherwise    = do
            a <- foo' (xs, n)
            b <- foo' (xs, n - w)
            return (a `max` b + v)
    
  3. 然后,我们在foo'中打开自引用(因为我们想调用memoized函数):

    type Endo a = a -> a
    
    foo' :: Monad m => Endo (([(Int, Int)], Int) -> Int)
    foo' _    ([],       _) = return 0
    foo' _    (_,        0) = return 0
    foo' self ((w,v):xs, n)
        | w > n             = foo' (xs, n)
        | otherwise         = do
            a <- self (xs, n)
            b <- self (xs, n - w)
            return (a `max` b + v)
    
  4. 我们将使用以下memoization mixin来记忆我们的函数foo'

    type Dict a b m = (a -> m (Maybe b), a -> b -> m ())
    
    memo :: Monad m => Dict a b m -> Endo (a -> m b)
    memo (check, store) super a = do
        b <- check a
        case b of
            Just b  -> return b
            Nothing -> do
                b <- super a
                store a b
                return b
    
  5. 我们的词典(备忘录表)将使用State monad和Map数据结构:

    import Prelude hiding (lookup)
    import Control.Monad.State
    import Data.Map.Strict
    
    mapDict :: Ord a => Dict a b (State (Map a b))
    mapDict = (check, store) where
        check a   = gets (lookup a)
        store a b = modify (insert a b)
    
  6. 最后,我们将所有内容组合在一起以创建一个记忆函数memoFoo

    import Data.Function (fix)
    
    type MapMemoized a b = a -> State (Map a b) b
    
    memoFoo :: MapMemoized ([(Int, Int)], Int) Int
    memoFoo = fix (memo mapDict . foo')
    
  7. 我们可以按如下方式恢复原始函数foo

    foo :: [(Int, Int)] -> Int -> Int
    foo xs n = evalState (memoFoo (xs, n)) empty
    
  8. 希望有所帮助。