How to memoize recursive functions?

后端 未结 2 1480
闹比i
闹比i 2021-01-02 06:09

Consider a recursive function, say the Euclid algorithm defined by:

let rec gcd a b =
  let (q, r) = (a / b, a mod b) in
  if r = 0 then b else gcd b r


        
相关标签:
2条回答
  • 2021-01-02 06:43

    The winning strategy is to define the recursive function to be memoized in a continuation passing style:

    let gcd_cont k (a,b) =
      let (q, r) = (a / b, a mod b) in
      if r = 0 then b else k (b,r)
    

    Instead of defining recursively the gcd_cont function, we add an argument, the “continuation” to be called in lieu of recursing. Now we define two higher-order functions, call and memo which operate on functions having a continuation argument. The first function, call is defined as:

    let call f =
        let rec g x =
          f g x
        in
        g
    

    It builds a function g which does nothing special but calls f. The second function memo builds a function g implementing memoization:

    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
    

    These functions have the following signatures.

    val call : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>
    val memo : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>
    

    Now we define two versions of the gcd function, the first one without memoization and the second one with memoization:

    let gcd_call a b =
      call gcd_cont (a,b)
    
    let gcd_memo a b =
      memo gcd_cont (a,b)
    
    0 讨论(0)
  • 2021-01-02 06:50
    # 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>
    

    You should read the section here: https://realworldocaml.org/v1/en/html/imperative-programming-1.html#memoization-and-dynamic-programming in the book Real World OCaml.

    It will help you truly understand how memo is working.

    0 讨论(0)
提交回复
热议问题