有没有办法在Haskell中记忆一个值?

时间:2011-08-26 20:59:36

标签: haskell functional-programming memoization

我在Haskell中有以下功能:

memdb = -- load the contents of a database into memory as a Map

然后我有以下一行:

map (\x -> memdb ! x) values

我希望memdb只生成一次Map,而不是每次map的迭代。我可以用这样的东西来做:

make_memdb = -- equivalent to memdb in previous example
memdb <- make_memdb
map (\x -> memdb ! x) values

但这意味着我必须将memdb传递给使用它的每个函数。我有什么办法可以:

一个。避免在每次调用时重新计算memdb

湾将make_memdb中生成的值保存为常量,以便我可以避免将其传递给使用它的每个函数?

3 个答案:

答案 0 :(得分:13)

由于您的地图来自数据库,这意味着它不能是常量,因为应用程序的运行可能会有所不同。

  

但这意味着我必须将memdb传递给使用它的每个函数。

是的,但是有一些工具可以让它听起来不那么糟糕。特别是,这听起来像是the reader monad的完美用例!

当您有一些值(如配置)时,通常会使用Reader monad,您希望在程序开始时加载它,然后能够在程序周围访问它而无需一直显式传递它。以下是您将如何使用它的简短示例:

main = do
    memdb <- make_memdb -- Get the memdb from the database once and for all
    runReaderT foo memdb

foo = do
    memdb <- ask -- Grab the memdb. Will not reload from the database
    liftIO $ putStrLn "Hello, world" -- IO actions have to be lifted
    -- [...]

另见:

答案 1 :(得分:1)

您似乎希望通过memdb获取IO作为避免传递更多参数的方法,对吗?然后,您询问是否可以(A)定义memdb,暗示它将是顶级函数,没有从数据库加载数据的开销,或者(B)如果您可以使用全局保存加载的数据结构范围。

这两个都可以用IORefunsafePerformIO来定义顶级可变全局变量。我不建议你这样做。重构是笨拙和讨厌的。那就是说,无论如何我都会告诉你:

假设你有一个功能:

make_memdb :: IO (Map K V)

您可以声明顶级可变变量:

import Data.Map as M
import Data.IORef

mRef :: IORef (Map K V)
mRef = unsafePerformIO $ newIORef M.empty
{-# NOINLINE mRef #-}

main = do
    m <- make_memdb
    writeIORef mRef m
    ... do  stuff using mRef ...

stuffUsingMRef ... = do
    memdb <- readIORef
    let vs = map (memdb !) values
    return vs

请注意,您的功能将永远存在于IO中。这是因为您需要IO才能读取放置memdb的全局可变变量。如果你不喜欢这个,并且你不喜欢传递参数,那么学习状态monad!我相信另一个答案会讨论这个问题,这是正确的解决方案。

答案 2 :(得分:0)