I'm learning about optimization in Composing Programs. I have 3 functions. memo
is for memoization technique, count
is a wrapper, so every time fib
is called a counter is activated. fib
is just an implementation of Fibonacci with recursivity.
def memo(f):
cache = {}
def memoized(n):
if n not in cache:
cache[n] = f(n)
return cache[n]
return memoized
def count(f):
def counted(*args):
counted.call_count = 1
return f(*args)
counted.call_count = 0
return counted
def fib(n):
if n == 0:
return 0
if n == 1:
return 1
return fib(n - 2) fib(n - 1)
To call the functions, I assing count(fib)
to counted_fib
, so every call is recorded. And* fib
*variable is reasigned (now, fib
is memo(counted_fib)
not the original function)
counted_fib = count(fib)
fib = memo(counted_fib)
My problem start when the function fib
is called (e.g. fib(19)
) Everything works as it should be, but the recursion inside the original function (return fib(n - 2) fib(n - 1)
) trigger fib
as memo(counted_fib)
.
This is not a problem about something that is not working. I'm just trying to understand why it happens if one thing is declared before another.
CodePudding user response:
If what you are confused about is the way that the fib
name behaves when being reassigned from the original fib()
function to being a variable calling memo(counted_fib
, I think this article about python's namespace behavior will help.
Python programs are executed starting from the first statement. But even before that, the global namespace is loaded. So you assign the fib name to fib()
. But later on, you change that assignment to memo(counted_fib)
, and that last assignment is the one loaded with the global namespace once the program starts.
Hope it helps!
CodePudding user response:
In python, everything is an Object, functions included.
When you declare def fib
it creates an object call fib
and this object is callable
.
This callable
is calling in self by reference.
When you re-declare fib = memo(counted_fib)
this move the reference fib to another object. (in this case memo function)
CodePudding user response:
The count() function keeps track of each time the fib function was called by incrementing a call_count variable which can be checked afterwards. Then memo(counted_fib) is used to provide caching facilities for the counted_fib function. It does so by maintaining a cache object that keeps track of already computed values, thus ensuring that expensive recursive calls to counted_fib will not be duplicated when its inputs are called multiple times. Since every time fib is called inside counted_fib, it gets counted, memo(countedfib) ensures that each counted_fib call will only run once and all its computations will get cached for future access. Thus, after assigning counted_fib it would be possible to make successive calls to fib without overwriting its original definition or spend more time in expensive recursive calls.