Reference 在哈希表中记录函数
我必须完成一个练习,其中 我编写了一个函数memo:(('a->'b)->'a->'b)->stats->'a->'b.函数memo将函数f(我们要记忆的函数)作为输入,记录stats类型(其类型声明如下所示),以跟踪我们访问本地存储的频率和添加的条目数量,并返回类型为“a->”b的函数 当使用“a”类型的输入调用此函数时,它将运行f,记录中间结果,并作为最终结果返回“b”类型的值。它还将相应地更新其stats记录中的值。例如,要创建一个将数字累加到x的memoizing函数,我们有memo(fungx->如果x=0,那么0 else x+g(x-1))stats 记录类型统计信息有两个字段: 条目:已记录的结果数 lkp:在存储中找到已记忆结果而不是执行函数的次数 注意给定函数f的类型:它本身需要另一个函数作为参数。具体来说,传递给f的函数g将是f在递归情况下调用的函数 我遇到的问题是,我的查找次数总是减少1或2次。如果有人能澄清为什么会发生这种情况或给我一个提示,我将不胜感激。下面是我的尝试,它计算函数调用的正确值和正确的条目数。希望下面的代码能让它更清晰:Reference 在哈希表中记录函数,reference,ocaml,memoization,Reference,Ocaml,Memoization,我必须完成一个练习,其中 我编写了一个函数memo:(('a->'b)->'a->'b)->stats->'a->'b.函数memo将函数f(我们要记忆的函数)作为输入,记录stats类型(其类型声明如下所示),以跟踪我们访问本地存储的频率和添加的条目数量,并返回类型为“a->”b的函数 当使用“a”类型的输入调用此函数时,它将运行f,记录中间结果,并作为最终结果返回“b”类型的值。它还将相应地更新其stats记录中的值。例如,要创建一个将数字累加到x的memoizing函数,我们有memo(f
type stats =
{ entries : int ref;
lkp : int ref }
let memo (f: (('a -> 'b) -> 'a -> 'b)) (stats: stats) : ('a -> 'b) =
let map = Hashtbl.create 1000 in
let rec g x =
match Hashtbl.find_opt map x with
| None -> let result = f (g) x in Hashtbl.add map x result ; stats.entries := !(stats.entries) + 1 ; result
| Some v -> stats.lkp := !(stats.lkp)+ 1 ; v
in stats.entries := !(stats.entries) + 1 ; f g
让我们对您的代码进行除臭:
type stats = {
entries : int ref;
lkp : int ref
}
let memo (f: (('a -> 'b) -> 'a -> 'b)) (stats: stats) : ('a -> 'b) =
let map = Hashtbl.create 1000 in
let rec g x =
match Hashtbl.find_opt map x with
| None ->
let result = f (g) x in
Hashtbl.add map x result;
incr stats.entries;
result
| Some v ->
incr stats.lkp;
v in
incr stats.entries;
f g
现在,我们可以很容易地看到,如果我们有一个缓存未命中(None
分支),我们会增加条目的数量(看起来正确),但不会增加查找的数量(看起来可疑,我仍然会将其视为一个查找,除非您所说的查找是指缓存命中)。当缓存命中(Some分支)时,我们会增加查找次数。到现在为止,一直都还不错。我们还可以看到,出于某种原因,在调用memo
时,我们无条件地增加了条目数。这看起来很奇怪,我们没有在这里添加任何条目。很可能是一些遗留代码
我希望剩下的都清楚了。主要的好处是,您应该尝试编写语法上易于理解的程序。不要害怕使用键盘上的回车键,不要试图一次做太多。另外,尝试使用支持自动缩进的IDE(vscode、emacs、vim)。这会让你免于令人不快的惊喜。谢谢,这看起来比我的好多了。我无条件地增加stats.entries,因为出于某种原因,当函数调用完成时,它似乎给了我正确的条目数,尽管我同意这没有意义。再加上我的查找次数比应该的多1或2,使我觉得有些案例没有被正确缓存,但我不确定。尽管缺少
incr stats.lkp
在miss case(None分支)中,我看不到您的代码有任何问题,当我运行它时,我得到了我期望的数字。啊,好的,不管怎样,还是要谢谢你的时尚提示。我在最后一行中用“g”替换了“fg”,并删除了最后一行中多余的增量操作,从而解决了我的问题。实际上,我认为在这种情况下,用g代替fg不会有什么不同,但令人惊讶的是did@JeffreyScofield,我认为您在错误的帖子中发布了一条评论:)(评论还可以,但代码现在已更正。)请不要修改问题,使现有答案无效。