【问题标题】:Memoize a Double function in Haskell在 Haskell 中记忆一个 Double 函数
【发布时间】:2013-07-16 19:02:57
【问题描述】:

我有一个函数

slow :: Double -> Double

它经常被调用(数亿次),但只被调用大约一千个离散值。这似乎是记忆的绝佳候选者,但我不知道如何记忆 Double 的函数。

制作列表的标准技术不起作用,因为它不是整数类型。我查看了 Data.MemoCombinators,但它本身并不支持 Doubles。有一个bits 函数用于处理更多数据类型,但Double 不是Data.Bits 的实例。

有没有一种优雅的方式来记忆“慢”?

【问题讨论】:

  • 如果你知道它会被提前调用的值,你可以使用Data.MapfromListmap。如果您不提前知道这些值,则需要某种惰性数据结构来表示“将slow 应用于Double 类型的所有可能值”。以理智的方式做到这一点的困难在于,目前Data.MemoCombinators 中没有任何内容。
  • 在编译时计算所有值,然后构建一个包含 1000 个分支的巨型 case 语句。没有比这更高效的了。
  • @GabrielGonzalez:我很乐意,但是直到运行时才知道 1000 个分支。

标签: haskell memoization


【解决方案1】:

您始终可以使用ugly-memo。内部结构不纯,但速度很快,可以满足您的需求(除非参数为 NaN)。

【讨论】:

    【解决方案2】:

    我认为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 ]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-20
      • 2021-12-23
      • 2011-07-29
      • 2016-04-11
      • 2020-10-25
      相关资源
      最近更新 更多