【问题标题】:Two parameter memoization in HaskellHaskell 中的两个参数记忆
【发布时间】:2011-07-29 23:57:31
【问题描述】:

我正在尝试记忆以下功能:

gridwalk x y
    | x == 0 = 1
    | y == 0 = 1
    | otherwise = (gridwalk (x - 1) y) + (gridwalk x (y - 1))

看着this我想出了以下解决方案:

gw :: (Int -> Int -> Int) -> Int -> Int -> Int
gw f x y
    | x == 0 = 1
    | y == 0 = 1
    | otherwise = (f (x - 1) y) + (f x (y - 1))

gwlist :: [Int]
gwlist = map (\i -> gw fastgw (i `mod` 20) (i `div` 20)) [0..]

fastgw :: Int -> Int -> Int
fastgw x y = gwlist !! (x + y * 20)

然后我可以这样称呼:

gw fastgw 20 20

是否有更简单、更简洁和通用的方法(注意我必须在gwlist 函数中硬编码最大网格尺寸以便从 2D 转换为 1D 空间,以便我可以访问记忆列表)来记忆函数Haskell 中有多个参数?

【问题讨论】:

    标签: haskell memoization


    【解决方案1】:

    这是使用MemoTrie 包中的Data.MemoTrie 来记忆函数的版本:

    import Data.MemoTrie(memo2)
    
    gridwalk :: Int -> Int -> Int
    gridwalk = memo2 gw
      where
        gw 0 _ = 1
        gw _ 0 = 1
        gw x y = gridwalk (x - 1) y + gridwalk x (y - 1)
    

    【讨论】:

      【解决方案2】:

      如果你想要最大程度的通用性,你可以记忆一个记忆函数。

      memo :: (Num a, Enum a) => (a -> b) -> [b]
      memo f = map f (enumFrom 0)
      
      gwvals = fmap memo (memo gw)
      
      fastgw :: Int -> Int -> Int
      fastgw x y = gwvals !! x !! y
      

      此技术适用于具有任意数量参数的函数。

      编辑:感谢 Philip K. 指出原始代码中的错误。最初 memo 有一个“有界”约束而不是“Num”,并从 minBound 开始枚举,这仅对自然数有效。

      但是,列表并不是一个很好的记忆数据结构,因为它们具有线性查找复杂性。使用 Map 或 IntMap 可能会更好。或lookonHackage

      请注意,此特定代码确实依赖于惰性,因此如果您想切换到使用 Map,则需要从列表中获取有限数量的元素,如下所示:

      gwByMap :: Int -> Int -> Int -> Int -> Int
      gwByMap maxX maxY x y = fromMaybe (gw x y) $ M.lookup (x,y) memomap
       where
        memomap = M.fromList $ concat [[((x',y'),z) | (y',z) <- zip [0..maxY] ys]
                                                    | (x',ys) <- zip [0..maxX] gwvals]
      
      fastgw2 :: Int -> Int -> Int
      fastgw2 = gwByMap 20 20
      

      我认为 ghc 在这种情况下共享可能很愚蠢,您可能需要去掉 xy 参数,如下所示:

      gwByMap maxX maxY = \x y -> fromMaybe (gw x y) $ M.lookup (x,y) memomap
      

      【讨论】:

      • 当 memo 需要 (a -&gt; b) 类型的函数但 gw(Int -&gt; Int -&gt; Int) 时,如何将 gw 传递给 memo?另外,minBound 应用于 Int 类型的东西时不会产生负索引吗?
      • @Philip K - a-&gt;b 中的 b 类型与 Int -&gt; Int 统一,记住函数生成器非常好。但是,负索引是一个错误。我将编辑我的答案。
      【解决方案3】:

      使用来自 hackage 的 data-memocombinators 包。它提供了易于使用的记忆技术,并提供了一种简单而简洁的使用方式:

      import Data.MemoCombinators (memo2,integral)
      
      gridwalk = memo2 integral integral gridwalk' where
        gridwalk' x y
          | x == 0 = 1
          | y == 0 = 1
          | otherwise = (gridwalk (x - 1) y) + (gridwalk x (y - 1))
      

      【讨论】:

      • 不应该 gridwalk 自己调用 gridwalkMemo 吗?
      • 我选择这个是因为简洁和投票计数,但是某事和约翰的回答都帮助我更好地理解了 Haskell 中的记忆,谢谢!
      • 您可能需要cabal install data-memocombinators
      【解决方案4】:

      您可以使用列表列表来记忆两个参数的函数结果:

      memo :: (Int -> Int -> a) -> [[a]]
      memo f = map (\x -> map (f x) [0..]) [0..]
      
      
      gw :: Int -> Int -> Int
      gw 0 _ = 1
      gw _ 0 = 1
      gw x y = (fastgw (x - 1) y) + (fastgw x (y - 1))
      
      gwstore :: [[Int]]
      gwstore = memo gw
      
      fastgw :: Int -> Int -> Int
      fastgw x y = gwstore !! x !! y
      

      【讨论】:

      • 这应该更高。虽然不像其他人那么容易,但这是迄今为止最有见地的答案。
      猜你喜欢
      • 2010-10-12
      • 2013-08-20
      • 2016-03-30
      • 1970-01-01
      • 2020-10-25
      • 2011-03-13
      • 1970-01-01
      • 2016-04-11
      相关资源
      最近更新 更多