【发布时间】:2023-02-26 10:12:30
【问题描述】:
我正在尝试编写一个函数,它给定一些函数 f memoizes f,这样当调用 g = memoize f 后跟 g x 时,所有后续调用函数 g 和参数 x 只返回缓存的结果。
但是,我正在努力想出一个改进以下所需的显式状态传递的实现:
memoize :: Ord t => (t -> a) -> Map t a -> t -> Map t a
memoize f m a = case Map.lookup a m of
Just _ -> m
Nothing -> Map.insert a (f a) m
用一个人为的例子来展示它的用法:
main :: IO ()
main = do
let memoPlusOne = memoize (+ 1) in
let m = memoPlusOne Map.empty 1
in let mm = memoPlusOne m 1
in print mm
我知道有 other, better ways to memoize functions in Haskell,但我的问题更关心改进将状态传递给函数的一般模式,以避免任何状态突变,否则这些突变会像其他语言一样被封装,例如正如 Ocaml 中的这个例子:
let memo_rec f =
let h = Hashtbl.create 16 in
let rec g x =
try Hashtbl.find h x
with Not_found ->
let y = f g x in
(* update h in place *)
Hashtbl.add h x y;
y
in
g
【问题讨论】:
-
在 OCaml 中,可变状态不是必需的。您只需要使用
Map.Make来创建一个地图仿函数并使用它来代替Hashtbl。 -
在 OCaml 中,我们可以随处使用副作用来改变数据,所以这不是问题。在 Haskell 中,副作用是被禁止的,所以它更复杂。必须要么利用惰性数据结构(不是严格的
Map)并可能获得次优性能,要么使用unsafe函数颠覆系统,以便无论如何执行变异/副作用。由于从外部无法观察到副作用(记忆函数表现为纯函数),因此不会破坏引用透明性。您可以查看 Hoogle/Hackage 中的记忆库并研究它们的方法。 -
我的建议是:不要重新发明轮子,也不要在严肃的代码中玩弄
unsafe东西。相反,采用现有的记忆库并使用它——这样您就不会冒造成严重破坏的风险。
标签: haskell state ocaml immutability