【问题标题】:Memoizing functions in a hash table在哈希表中记忆函数
【发布时间】:2021-01-30 09:02:44
【问题描述】:

我必须完成一个练习

我写了一个函数 memo : (('a -> 'b) -> 'a -> 'b) -> stats -> 'a -> 'b. 函数 memo 需要作为输入一个函数 f(我们想要记忆的函数),一个类型统计的记录(它的类型声明在下面给出)来跟踪我们访问本地存储的频率和我们添加了多少条目,并返回一个函数输入'a -> 'b。

当使用 'a 类型的输入调用此函数时,它将运行 f,记忆中间结果,并作为最终结果返回 'b 类型的值。它还将相应地更新其统计记录中的值。例如,要创建一个将数字相加到 x 的记忆函数,我们有 memo (fun g x -> if x=0 then 0 else x + g(x-1)) stats

记录类型统计有两个字段:

entries:已记忆的结果数

lkp:在 store 中找到 memoized 结果而不是执行函数的次数

注意给定函数 f 的类型:它本身需要另一个函数作为参数。具体来说,传递给 f 的函数 g 将是 f 将在其递归情况下调用的函数。

我遇到的问题是我的查找次数总是减少 1 或 2。如果有人能澄清为什么会发生这种情况或给我一个提示,我将不胜感激。下面是我的尝试,它计算函数调用的正确值和正确的条目数。希望下面的代码能让它更清晰:

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

【问题讨论】:

  • 请不要以使现有答案无效的方式修改问题。

标签: reference ocaml memoization


【解决方案1】:

让我们去混淆你的代码:

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 时,我们会无条件地增加条目数。这看起来很奇怪,我们不在这里添加任何条目。很可能是一些遗留代码。

我希望其余的都清楚。主要的收获是您应该尝试编写语法上易于理解的程序。不要害怕使用键盘上的 Enter 按钮,不要尝试一次制作太多。另外,尝试使用支持自动缩进的 IDE(vscode、emacs、vim)。它将使您免于令人讨厌的惊喜。

【讨论】:

  • 谢谢,这看起来比我拥有的好多了。我在那里无条件地增加 stats.entries ,因为由于某种原因,当函数调用完成时,这似乎给了我正确的条目数,尽管我同意这没有意义。这加上我的查找次数比应有的多 1 或 2 使我认为某些情况没有被正确缓存,但我不确定。
  • 尽管在未命中的情况下缺少incr stats.lkp(None 分支),但我没有看到您的代码有任何问题,当我运行它时,我得到了我期望的数字。
  • 嗯,好的,无论如何感谢您的风格提示。我设法通过在最后一行用“g”替换“f g”来解决我的问题,并在最后一行删除了多余的增量操作。我实际上认为在这种情况下用 g 替换 f g 不会有什么不同,但令人惊讶的是它确实
  • @JeffreyScofield,我认为您对错误的帖子发表了评论 :)
  • (评论没问题,但现在代码已经更正了。)
猜你喜欢
  • 2016-03-25
  • 2011-02-27
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-24
  • 2021-01-23
相关资源
最近更新 更多