我有一个递归函数f
,它有两个参数x
和y
。该函数由第一个参数唯一确定;第二个只是让事情变得更容易。
我现在想要记住那个函数w.r.t.它是第一个参数而忽略了第二个参数。 (即f
对x
}
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参数。
答案 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
:
首先,我们创建一个未经验证的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
接下来,我们对函数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)
然后,我们在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)
我们将使用以下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
我们的词典(备忘录表)将使用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)
最后,我们将所有内容组合在一起以创建一个记忆函数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')
我们可以按如下方式恢复原始函数foo
:
foo :: [(Int, Int)] -> Int -> Int
foo xs n = evalState (memoFoo (xs, n)) empty
希望有所帮助。