缓存在运行时构造的函数的可能值

时间:2012-03-23 12:25:47

标签: performance caching haskell lazy-evaluation

我有一个带有一些值构造函数的数据构造函数:

data DataType = C1 | C2 | C3 | ... | Cn

我想在运行时从该数据类型到其他一些值构建一个函数(事实上,我在IO monad中这样做):

buildFun :: IO (DataType -> b)
buildFun = do
    ....
    return $ \x -> case x of
                       C1 -> someProcessesToGetTheValue C1
                       ...
                       Cn -> someProcessesToGetTheValue Cn

这是否意味着每次调用返回的函数时都会调用someProcessesToGetTheValue?

我更喜欢Haskell来评估buildFun中的someProcessesToGetTheValue(因为那些调用非常昂贵)并返回一个返回这些完全计算表达式的函数。

我可以强制这种行为吗?也许通过做类似以下的事情?:

buildFun :: IO (DataType -> b)
buildFun = do
    C1value <- return $ someProcessesToGetTheValue C1
    ...
    Cnvalue <- return $ someProcessesToGetTheValue Cn
    return $ \x -> case x of
                       C1 -> C1value
                       ...
                       Cn -> Cnvalue

2 个答案:

答案 0 :(得分:4)

您根本不必涉及IO monad(事实上do { x <- return v; ... }let x = v in ...相同),只需绑定lambda之外的值:

buildFun :: IO (DataType -> b)
buildFun = do
    let v1 = someProcessesToGetTheValue C1
    ...
    return $ \x -> case x of { C1 -> v1; ... }

Haskell并没有真正指定有关运行时评估行为的任何内容,但是在所有常见实现中,这将确保结果 shared ;有关详细信息,请参阅What does "floated out" mean?

但是,它仍然不会评估 v 1 ... v n 里面buildFun;相反,它们将在第一次评估您返回的函数的相应结果时进行评估。如果您想强制对它们进行预先评估,可以说let !v1 = someProcessesToGetTheValue C1(这需要BangPatterns语言扩展名)或v1 <- evaluate $ someProcessesToGetTheValue C1(来自Control.Exception;这表现得很好如果someProcessesToGetTheValue C1可能抛出异常,则会更好。)

答案 1 :(得分:0)

而不是函数,为什么不改为定义一些数据结构,比如列表,评估这个函数的所有结果(由数据类型中的构造函数的位置索引)?例如,像这样(未经测试):

data DataType = C1 | C2 | C3 | ... | Cn deriving (Enum, Bounded)

cachedValues :: [b]
cachedValues = map someProcessesToGetTheValue ([minBound .. maxBound] :: [DataType])

getCachedValue :: DataType -> b
getCachedValue x = cachedValues !! (fromEnum x)

由于Haskell是懒惰的,它会存储一个thunk直到它第一次运行,之后它会记住该值。

(如果列表遍历大小为n的列表效率低下;您可以使用数组或Map代替。这个想法是相同的。)