我有一个功能
slow :: Double -> Double
经常被调用(数亿次),但只能调用大约一千个离散值。这似乎是备忘录的绝佳候选者,但我无法弄清楚如何记忆Double的功能。
制作列表的标准技术不起作用,因为它不是整数类型。我查看了Data.MemoCombinators,但它本身并不支持双打。有bits
函数用于处理更多数据类型,但Double
不是Data.Bits
的实例。
有没有一种优雅的方式来记住`慢?
答案 0 :(得分:6)
您可以随时使用ugly-memo。内部是不纯的,但它很快并且做你需要的(除非参数是NaN)。
答案 1 :(得分:3)
我认为StableMemo
应该完全符合您的要求,但我对此没有任何经验。
有两种主要方法:使用Ord
属性将密钥存储在树结构中,如Map
。这不需要您需要的整体属性,例如:一个MemoTrie
方法;它因此变慢但很简单。
另一种适用于更常见类型的替代方法是将无序地映射到具有Hash function的大型整数域,以便将密钥存储在hash map中。由于HashMap
的界面与Map
的界面基本匹配,因此速度会快得多,但同样简单,所以你可能想要这样做。
现在,遗憾的是完全与MemoCombinators
一样简单。它直接建立在IntTrie
上,专门用于提供惰性/无限/纯接口。相比之下,Map
和特别HashMap
都可以很好地用于不纯的记忆,但本身并不能纯粹地做到这一点。你may throw in some UnsafePerformIO
(呃哦),或者只是在IO
monad(yuk!)中公开地做。或者使用StableMemo
。
但如果您已经知道它将会是哪个值,那么它实际上是简单而安全的,至少大多数调用在编译时。然后你可以在开头用这些值填充本地哈希映射,并在每次调用时查找它是否存在,否则只需直接调用昂贵的函数:
import qualified Data.HashMap.Lazy as HM
type X = Double -- could really be anything else
notThatSlow :: Double -> X
notThatSlow = \v -> case HM.lookup v memo of
Just x -> x
Nothing -> slow v
where memo = HM.fromList [ (v, x) | v<-expectedValues, let x = slow v ]