使用预制数据结构在Haskell中进行记忆

时间:2011-04-01 13:18:02

标签: haskell memoization

我发现this answerthis wiki page是Haskell中记忆化的绝佳介绍。但是,他们确实给我留下了一个我希望得到回答的问题:

在我看来,所使用的技术要求您“打开”(如“访问内部”)用于存储备忘录的数据结构。例如,1实现了表结构,2实现了section 3中的树。是否可以使用预先制作的数据结构进行类似的操作?例如,假设您认为Data.Map非常棒,并希望将您的memoized值存储在Map中。可以用一个预制的数据结构来实现memoization,其中一个实现结构本身,而是使用预先制作的结构?

希望有人会给我一个关于如何思考,或者更有可能纠正我对功能性记忆的误解的暗示。

编辑:可以想到一种方法,但它并不优雅:如果f :: a -> b,那么可以轻松制作一个memoized version f' :: Map a b -> a -> (Map a b, b),其中第一个参数是memoization存储,输出对包含可能更新的存储和计算值。这种状态传递肯定不是我想要的(尽管我猜它可能包含在monad中,但它比12中的方法更加丑陋。

编辑2:也许尝试表达我当前的(错误的)思维方式会有所帮助。目前,我似乎反复将我自己违背自己的意愿拉入非解决方案

import qualified Data.Map as Map
memo :: (Ord a) => [a] -> (a -> b) -> (a -> b)
memo domain f = (Map.!) storage
    where
      storage = Map.fromList (zip domain (map f domain))

我越是盯着这一点,我越发现我误解了一些基本的东西。你知道,我觉得我的memo [True, False]等同于1bool个回忆录。

1 个答案:

答案 0 :(得分:3)

如果您注意到,Data.Memocombinators实际上依赖于“预先制作的”Data.IntTrie。我确信您可以使用相同的代码并将IntTrie的使用替换为另一种数据结构,尽管它可能效率不高。

记忆的一般想法是保存计算结果。在Haskell中,最简单的方法是将函数映射到一个表,其中表每个参数有一个维度。由于Haskell是懒惰的(好吧,Haskell中的大多数数据结构都是),它只会在您特别要求时评估给定单元格的值。 “table”基本上是指“map”,因为它会将你从key(s)带到value。


[edit]关于示例2的其他想法

如果我没有弄错,那么第一次(Map.!) storage被迫为给定的密钥进行评估时,storage结构将被完全拧干(尽管计算f赢了除了给定的密钥之外的任何事情都会发生。因此,第一次查找将导致额外的O(n)操作,n为length domain。后续查找不会产生此费用。

类似于典型的int-indexed列表或IntTrie的Lazier结构同样需要在调用查找时显示其结构,但与Map不同,它们不需要一次完成。列表被拧干,直到访问索引键。 IntTries只拧出所需键的“前缀”(或后缀?不确定。可能以任何方式实现)的整数键。索引11,(1011)会拧出1(1),2(10),5(101)和11(1011)。 Data.Memocombinators只是将所有密钥转换为Ints(或“位”),以便可以使用IntTrie

P.S。是否有一个比“拧干”更好的短语?脑海中浮现出“武力”,“脊椎”和“表现”等字样,但我无法想到正确的词/短语。