I am studying dynamic programming and I wrote the following as an exercise
let rec fibonacci =
let cache : (int,int) Hashtbl.t = Hashtbl.create 100 in
fun n -> if n <= 1 then 1 else
match Hashtbl.get_opt n with
| Some x -> x
| None -> let x = (fibonacci (n-1)) + (fibonacci (n-2)) in
Hashtbl.add cache n x;
x
When I came back to it later I thought for a moment it might be buggy, and that I should rewrite it as
let fibonacci =
let cache : (int,int) Hashtbl.t = Hashtbl.create 100 in
let rec f n =
if n <= 1 then 1 else
match Hashtbl.find_opt cache n with
| Some x -> x
| None -> let x = (f (n-1)) + (f (n-2)) in
Hashtbl.add cache n x;
x
in
f
I had to test the first bit of code and time it to make sure it was running in linear time.
After all, why doesn’t the first bit of code reallocate a new cache every time fibonacci is called?
I get it after thinking about it for a while but it’s an interesting distinction between thinking of a recursive function call as a “goto” - i.e., go back to the first line of the function - and thinking about a recursive declaration as binding a name to a value which is permitted to refer to itself.
It’s pretty tricky, I think. I understand why let f = let x = (imperative code) in fun x → e only causes the imperative code to run once at the time of declaration of f, but it seems somehow trickier to convince myself of this when f is recursive.