Is it possible to combine memoization and tail-recursion somehow? I’m learning F# at the moment and understand both concepts but can’t seem to combine them.
Suppose I have the following memoize function (from Real-World Functional Programming):
let memoize f = let cache = new Dictionary<_, _>()
(fun x -> match cache.TryGetValue(x) with
| true, y -> y
| _ -> let v = f(x)
cache.Add(x, v)
v)
and the following factorial function:
let rec factorial(x) = if (x = 0) then 1 else x * factorial(x - 1)
Memoizing factorial isn’t too difficult and making it tail-recursive isn’t either:
let rec memoizedFactorial =
memoize (fun x -> if (x = 0) then 1 else x * memoizedFactorial(x - 1))
let tailRecursiveFactorial(x) =
let rec factorialUtil(x, res) = if (x = 0)
then res
else let newRes = x * res
factorialUtil(x - 1, newRes)
factorialUtil(x, 1)
But can you combine memoization and tail-recursion? I made some attempts but can’t seem to get it working. Or is this simply not possible?
As always, continuations yield an elegant tailcall solution:
There are two kinds of tests. First, this demos that calling F(4) caches F(4), F(3), F(2), F(1) as you would like.
Then, comment out the
***printf and uncomment the final test (and compile in Release mode) to show that it does not StackOverflow (it uses tailcalls correctly).Perhaps I’ll generalize out ‘memoize’ and demonstrate it on ‘fib’ next…
EDIT
Ok, here’s the next step, I think, decoupling memoization from factorial:
EDIT
Ok, here’s a fully generalized version that seems to work.