Recursion 如何记忆递归函数?

Recursion 如何记忆递归函数?,recursion,ocaml,memoization,Recursion,Ocaml,Memoization,考虑一个递归函数,比如欧几里德算法,定义如下: let rec gcd a b = let (q, r) = (a / b, a mod b) in if r = 0 then b else gcd b r (这是一个简化的、非常脆弱的定义。)如何记忆这样一个函数?定义高阶函数的经典方法memoize:('a->'b)->('a->'b) 在函数中添加备忘录是没有用的,因为它只会在第一次调用时节省时间 我发现了有关如何在Lisp或Haskell中记忆此类函数的详细信息: 这些建

考虑一个递归函数,比如欧几里德算法,定义如下:

let rec gcd a b =
  let (q, r) = (a / b, a mod b) in
  if r = 0 then b else gcd b r
(这是一个简化的、非常脆弱的定义。)如何记忆这样一个函数?定义高阶函数的经典方法
memoize:('a->'b)->('a->'b)
在函数中添加备忘录是没有用的,因为它只会在第一次调用时节省时间

我发现了有关如何在Lisp或Haskell中记忆此类函数的详细信息:


这些建议依赖于Lisp中覆盖函数符号定义的能力或Haskell使用的“按需调用”策略,因此在OCaml中没有用处。

获胜的策略是定义要以连续传递方式记忆的递归函数:

我们没有递归地定义
gcd_cont
函数,而是添加了一个参数,即调用“continuation”来代替递归。现在我们定义了两个高阶函数,
call
memo
,它们对具有continuation参数的函数进行操作。第一个函数,
call
定义为:

let call f =
    let rec g x =
      f g x
    in
    g
它构建了一个函数
g
,该函数除了调用
f
之外,没有任何特殊功能。第二个函数
memo
构建一个函数
g
实现备忘录化:

let memo f =
    let table = ref [] in
    let compute k x =
      let y = f k x in
      table := (x,y) :: !table; y
    in
    let rec g x =
      try List.assoc x !table
      with Not_found -> compute g x
    in
    g
let gcd_call a b =
  call gcd_cont (a,b)

let gcd_memo a b =
  memo gcd_cont (a,b)
这些函数具有以下签名

val call : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>
val memo : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>
#让我们记住f=
让table=Hashtbl.Poly.create()在
(乐趣x->
将Hashtbl.find表x与
|一些y->y
|无->
设y=fx
Hashtbl.add_exn table~ key:x~ data:y;
Y
);;
val备忘录:('a->'b)->'a->'b=
#让我们记录下诺雷克x=
让fref=ref(fun u->assert false)进入
让f=memoize(funx->f_norec!frefx)在
fref:=f;
f x
;;
val备忘录记录:(('a->'b)->'a->'b)->'a->'b=
您应该阅读以下部分:在《真实世界OCaml》一书中


它将帮助你真正理解备忘录的工作原理。

这个答案如何改进迈克尔·格吕纽瓦尔德的答案?@AdèleBlanc Sec指出了《真实世界ocaml》一书中的一个正式解释。它确实详细地解释了
memo
的工作原理。老实说,
连续传球风格
在迈克尔的回答中有点复杂,如果你不真正理解什么是
连续传球
let gcd_call a b =
  call gcd_cont (a,b)

let gcd_memo a b =
  memo gcd_cont (a,b)
# let memoize f =
    let table = Hashtbl.Poly.create () in
    (fun x ->
      match Hashtbl.find table x with
      | Some y -> y
      | None ->
        let y = f x in
        Hashtbl.add_exn table ~key:x ~data:y;
        y
    );;
val memoize : ('a -> 'b) -> 'a -> 'b = <fun>


# let memo_rec f_norec x =
    let fref = ref (fun _ -> assert false) in
    let f = memoize (fun x -> f_norec !fref x) in
    fref := f;
    f x
  ;;
val memo_rec : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>