我最近一直在玩MemoCombinators和MemoTrie软件包,而我正在尝试记忆一个正在采用树的函数(由于共享了几个节点,这实际上是伪装的DAG)。形式为:
data Tree k a = Branch (Tree k a) (k, a) (Tree k a) | Leaf (k, a)
所以我想记住一个类型的函数(基于它的键):
Tree k a -> b
现在我有一个模糊的理解,即这些记忆组合器用于将您的函数f :: a -> a
转换为a
的惰性(未评估)值的结构,因此当您拉出一个时,它已经是评估。所以这对我的树来说不会有问题 - 不知怎的,我需要把它变成一个由k
索引的值结构。
我无法弄清楚如何使用组合子库。一个简单的方法是创建一个函数k -> a
,它可以对地图进行索引,这个函数很合适,但看起来有点笨拙。
我是否误入了这个目标,还是我错过了一些明显的东西?
我可以很容易地看到如何用这种样式编写这个函数,通过所有计算显式地线程化我的“表”:
f :: Tree Int Int -> Map Int Int -> (Int, Map Int Int)
f (Branch l (k, x) r) m | (Just r) <- lookup k m = r
| otherwise = (max rl rr, m'')
where
(rl, m') = (f l m)
(rr, m'') = (f r m')
但那不太好。
答案 0 :(得分:5)
因此,大多数记忆技术都使用状态。 memoized版本的函数 保持集合映射输入到memoized输出。当它获得输入时,它会检查集合, 如果可用,返回memoized值。否则,它使用原始计算输出 函数的版本,将输出保存在集合中,并返回新记忆的输出。 因此,memoized集合在函数的整个生命周期内都会增长。
Haskell的备忘录就像你提到的那些回避状态,而是预先计算数据结构 持有记忆输出的集合,使用懒惰来确保特定的价值 直到需要时才计算输出。除了几个关键点之外,这与有状态方法有很多共同之处:
手动实施相当简单:
module Temp where
import Prelude hiding (lookup)
import Control.Arrow ((&&&))
import Data.Map (fromList, lookup)
data Tree k a = Branch (Tree k a) (k, a) (Tree k a) | Leaf (k, a)
key :: Tree k a -> k
key (Leaf (k, _)) = k
key (Branch _ (k,_) _) = k
-- memoize a given function over the given trees
memoFor :: Ord k => [Tree k a] -> (Tree k a -> b) -> Tree k a -> b
memoFor ts f = f'
where f' t = maybe (f t) id $ lookup (key t) m
m = fromList $ map (key &&& f) ts
MemoCombinators和MemoTrie包尝试做的是隐式输入集合(使用函数 和类型类,repsectively)。如果可以枚举所有可能的输入,那么我们可以 使用该枚举来构建我们的数据结构。
在您的情况下,由于您只想记住树木的key
,最简单的方法可能是
要使用MemoCombinators包中的wrap
函数:
wrap ::(a - &gt; b) - &gt; (b - &gt; a) - &gt;备忘录a - &gt;备忘录b
给出a和b之间的a和同构的memoizer,为b构建一个memoizer。
因此,如果您的key
值具有相应的Memo
值(例如,type Key = Int
),
你有一个从Key
到Tree Key Val
的双射,然后你可以使用
为您的Tree Key Val
函数制作备忘录的双向投射:
memoize :: (Tree Key Val -> b) -> (Tree Key Val -> b)
memoize = wrap keyToTree treeToKey memoForKey
更新:如果你不能提前创建这样的映射,也许解决方案是使用状态monad,这样你就可以随时记忆:
{-# LANGUAGE FlexibleContexts #-}
-- ...
import Control.Monad.State (MonadState, gets, modify)
import Data.Map (Map, insert)
-- ...
memoM :: (Ord k, MonadState (Map k b) m) => (Tree k a -> m b) -> (Tree k a -> m b)
memoM f = f'
where f' t = do
let k = key t
v <- gets $ lookup k
case v of
Just b -> return b
Nothing -> do
b <- f t
modify $ insert k b
return b
-- example of use
sumM :: (Ord k, MonadState (Map k Int) m) => Tree k Int -> m Int
sumM = memoM $ \t -> case t of
Leaf (_,b) -> return b
Branch l (_,b) r -> do
lsum <- sumM l
rsum <- sumM r
return $ lsum + b + rsum