let rec fib = function
| 0 -> 0
| 1 -> 1
| n -> fib (n-1) + fib (n-2)
This works, but it is quite inefficient as many intermediary results are calculated over and over again. It would be great to have a caching version of this function.
One of the things I like about F is the ability to easily and generically create caching versions of functions. Below is a possible implementation:
let cached f=
let cache=new Dictionary<_,_>()
fun a -> let ok,b=cache.TryGetValue a
if ok then b
else let result=f a
lock cache (fun ()->cache.[a]<-result)
  result
However: this doesn't help us much, because applying this function caches only the final result and not any intermediary results. The only way to start caching intermediary results is to break up the fib function:
let splitFib sub n = match n with
| 0 -> 0
| 1 -> 1
| _ -> sub (n-1) + sub (n-2)
We have substituted the recursive call by a call to a function parameter. Conceptually we would like to define our caching version of Fibonacci as:
let rec cachedFib = splitFib cachedFib |> cached
However, the F compiler will not tolerate this, raising "The value 'cachedFib' will be evaluated as part of its own definition." Therefore we are forced to explicitly mention to the compiler that the evaluation of cachedFib actually occurs at runtime only:
let rec cachedFib = splitFib (fun n->cachedFib n) |> cached
So we have established a simple 2 step procedure to create caching versions of recursive functions:
1. Create a "split" function by using a function parameter to handle all recursive calls
2. Use this "split" function to create a caching version by supplying an indirect recursive argument
No comments:
Post a Comment