嗯,有Data.HashTable
。不过,哈希表往往不能很好地处理不可变数据和引用透明性,所以我认为它没有多大用处。
对于少量值,将它们存储在搜索树中(例如Data.Map
)可能就足够快了。如果您可以忍受对您Double
的 s 进行一些修改,那么更强大的解决方案是使用类似 trie 的结构,例如Data.IntMap
; 它们的查找时间主要与密钥长度成正比,并且在集合大小上大致恒定。如果Int
限制太多,您可以在 Hackage 上四处挖掘以找到在使用的密钥类型方面更灵活的 trie 库。
至于如何缓存结果,我认为您想要的通常称为“memoization”。如果您想按需计算和记忆结果,该技术的要点是定义一个包含所有可能结果的索引数据结构,这样当您要求特定结果时,它只强制执行获得答案所需的计算你要。常见的例子通常涉及到列表的索引,但同样的原则应该适用于任何非严格的数据结构。根据经验,非函数值(包括无限递归数据结构)通常会被运行时缓存,而不是函数结果,因此诀窍是将所有计算包装在一个顶层定义中取决于任何论点。
编辑: MemoTrie 示例啊喂!
这是一个快速而肮脏的概念证明;可能存在更好的方法。
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
import Data.MemoTrie
import Data.Binary
import Data.ByteString.Lazy hiding (map)
mangle :: Double -> [Int]
mangle = map fromIntegral . unpack . encode
unmangle :: [Int] -> Double
unmangle = decode . pack . map fromIntegral
instance HasTrie Double where
data Double :->: a = DoubleTrie ([Int] :->: a)
trie f = DoubleTrie $ trie $ f . unmangle
untrie (DoubleTrie t) = untrie t . mangle
slow x
| x < 1 = 1
| otherwise = slow (x / 2) + slow (x / 3)
memoSlow :: Double -> Integer
memoSlow = memo slow
请注意 MemoTrie 包使用的 GHC 扩展;希望这不是问题。将其加载到 GHCi 中并尝试使用 (10^6) 或 (10^7) 之类的调用slow
vs.memoSlow
以查看它的实际效果。
将其推广到接受多个参数或诸如此类的函数应该相当简单。有关使用 MemoTrie 的更多详细信息,您可能会发现其作者的这篇博文很有帮助。