我有一个带有一些值构造函数的数据构造函数:
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
答案 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
代替。这个想法是相同的。)