Can't understand assignments and environments usage on this recursive function - python-3.x

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.

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!

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)

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.

Related

Functions - Variables - Scope

I have the below code.
a = 10
def f(x):
return x + a
a = 3
f(1)
If we print(f1) the result is 4. It seems that the second assignment of a is used here (a=3). Could someone explain to me if there is a particular reason or rule for not using the first assignment (a=10)?
Thank you,
Dimitrios
a and x are resolved each time return x + a is executed. You reassign a before calling f. f knows that a is not a local variable so looks up "a" in its enclosing scope. That "a" has a 3 in it, and that is what is used.
As a general rule, it would be very bad for f to use the original assignment when its executed. That would mean that functions couldn't take advange of global variables changing. It would also require that global variables be assigned before the function is even defined, which is not particularly useful for a dynamic language.
You can hack it, though. If you define a parameter with a default value, it will be assigned when the function is defined and you get what you want.
a = 10
def f(x, a=a):
return x + a
a = 3
print(f(1))
You see, you've defined a before you defined f(x). But your call to f(1) was after you assigned the value 3 to "a".
f(x) will always use the current value of "a" when it its called, not when it is defined. If you want a constant value for "a" inside f(x), you should declare it inside the function, something like:
def f(x):
a = 10
return x + a

Flow of a factorial function using recursion

I'm learning the fundamentals of Python right now by reading Thinking Python.
As I was reading a chapter about "fruitful" function, that is, a function that "returns" a value, I came across this problem. I can more or less figure out how the code of the factorial function works, but not exactly. I have trouble in understanding precisely the flow of a function involving self-calling or recursion. Btw, the printer statments in the code shown as below are scaffolding, a device used to track the flow of the function.
Without further ado, here is the code.
def factorial(n):
space=' '*(2*n)
print(space, 'factorial', n)
if n==0:
print(space, 'returning 1')
return 1
else:
recurse = factorial(n-1)
result=n*recurse
print(space, 'Returning', result)
return result
factorial(3)
The result or the output of this code is shockingly a v-shape. I, for the life of me, cannot figure out why the computer (or the compiler?) runs the program the way it does here. It seems like it firstly prints the first line and the last line, and then goes on to print the second line and the second to the last line...This is strange. You would expect that it always goes top down.
To illustrate the oddity of this issue, I hereby attach another piece of code that prints numbers as you count down. The code is similar and simpler, and you would expect it to behave similarly to the factorial function before. But it doesn't. There is no oddity in the order in which the lines of outcome are displayed or printed off.
def f(n):
print(n)
if n==0:
return 0
else:
print(3*n)
f(n-1)
return
I would really appreciate it if you could help me with this!
What might confuse you is that the recursive call does not happen at the end of the function as it is the case with your example f(). Which means, you have code that is executed before stepping into the recursion and code that is executed after stepping out of the recursion. Let's have a look at what happens in factorial:
The recursive step
Pick any recursion step i you like, which is not a base case. And then look at what the function does, without following through with the recursion.
For your factorial function, let's take the call factorial(i), where iis different from 0. That call does three things:
print 2*i spaces and "factorial i"
call factorial(i-1)
print 2*i spaces and "returning i"
So, the output is going to be:
(2*i spaces) factorial i
# everything factorial(i-1) outputs
(2*i spaces) returning i
You'll see that the output of factorial(i-1) gets sandwiched between the outputs of factorial(i). To perform the recursion, all you need to do is blow the pattern up. Let's take another step i-1 into account:
(2*i spaces) factorial i
(2*(i-1) spaces) factorial i-1
# everything factorial(i-1) outputs
(2*(i-1) spaces) returning i-1
(2*i spaces) returning i
So, you see that the indentation decreases with the calls since i decreases, which accounts for the v-shape.
The base case
It just prints
factorial 0
returning 1
Putting it all together
Now, you can find a verbal expression for what factorial(i) does:
factorial(i) produces the v-shape output
factorial i
factorial i-1
...
factorial 0
returning 1
...
returning i-1
returning i
With it being true for the case i == 0, conclude from there that is is true for i+1 if you assume that is is correct for i using the list in the section recursive step. Done that, you can be sure that is how factorial(n) works for any n >= 0.
Here's your code a bit restructured to make it easier to see:
def factorial(n):
space = ' '*(2*n)
print(space, 'factorial', n)
result = 1 if n == 0 else n*factorial(n-1)
print(space, 'Returning', result)
return result
And that's the output for factorial(3) with the "V shape" you mentioned:
factorial 3
factorial 2
factorial 1
factorial 0
Returning 1
Returning 1
Returning 2
Returning 6
It looks correct and does the job of calculating the factorial. To understand the order of prints seen here, think of the following:
The prints start with the call for 3, since that's your entry point
The indentation is then 3x2 = 6 spaces
Since for the calculation of factorial(3) the reuslt of factioral(2) is needed, this call comes next, with an indentation of 2x2 = 4 spaces. And so on for all further factorials. That's where the V shape of the first half of prints comes from.
All of these function calls are now "waiting" for a result to return, at the position of the recursive call in the code. Since only factorial(0) can deliver that return value without another recursion, it will print the first return value. With an indentation of 0.
With the above return value, the function call to factorial(1) can now calculate its result by multiplying the returned 1 with its own n, which gives 1. That's the next print, with an indentation of 2.
And so on for all the higher recursion steps, from the inside to the outside. And that's how the second half of the V shape is formed.
The vital thing to understand is that the result values of the functions (and thereby the print) can only occur at the END of the complete recursion tree that comes below it. That's why the call print factorial 3 is in the first line, while its corresponding Returning 6 print is at the last line - after the whole inner tree is finished.
Hard to say if I picked up the crunchpoint you had trouble understanding with, but I hope this somehow helps.
EDIT: infinite recursion
As an answer to your question, here's an example that shows how each recursive function call will have its own variable scope and therefore not know about the variables set in an another instance of the same function, ending up in a RecursionError when the maxiumum recursion depth of the call stack is reached.
def foo():
try:
print("a = ", a)
return # break out of loop
except UnboundLocalError:
print("no variable a. defining one now.")
a = 3
foo() # try again
foo()

Why UnboundLocalError doesn't occur with lists?

I have a question regarding a tutorial problem.
Write a function make_monitored that takes as input a function, f, that itself takes one input. The result returned by make_monitored is a third function, say mf, that keeps track of the number of times it has been called by maintaining an internal counter. If the input to mf is the special string "how-many-calls?", then mf returns the value of the counter. If the input is the special string "reset-count", then mf resets the counter to zero. For any other input, mf returns the result of calling f on that input and increments the counter.
I have the following solution which works, surprisingly.
def make_monitored(f):
calls=[0]
def helper(n):
if n=="how-many-calls?":
return calls[0]
elif n=="reset-count":
calls[0]=0
else:
calls[0]+=1
return f(n)
return helper
I recalled reading about UnboundLocalError here: UnboundLocalError in Python
My question would be why won't calls[0]+=1 trigger that error? I made an assignation to a variable outside the local scope of the third function helper , and it seems a similar solution that uses instead calls.append(1) (the rest of the code correspondingly becomes len(calls) and calls.clear()) also bypasses that error.

does this Elm Fibonacci example need to be memoized?

Here is the Fibonacci code on the Elm syntax page. Just curious does recursion need to be memoized or does lazy evaluation take care of it?
fib n = case n of
0 -> 1
1 -> 1
_ -> fib (n-1) + fib (n-2)
In other languages (such as Python) the number of function calls would grow exponentially in n so that in if f(30) would compute f(10) like 4000 times or someting.
Haskell How is this fibonacci-function memoized?
Python What is memoization and how can I use it in Python?
Viewing the compiled source (as of Elm 0.18), you'll see that the Elm code gets transpiled down to the following javascript code (the variable names may differ).
var _user$project$Temp1483721371847322$fib = function (n) {
var _p0 = n;
switch (_p0) {
case 0:
return 1;
case 1:
return 1;
default:
return _user$project$Temp1483721371847322$fib(n - 1) + _user$project$Temp1483721371847322$fib(n - 2);
}
};
Javascript does no automatic memoization since function calls are not guaranteed to be deterministic. If you require memoization, you will have to roll your own.

How to create an array of functions which partly depend on outside parameters? (Python)

I am interested in creating a list / array of functions "G" consisting of many small functions "g". This essentially should correspond to a series of functions 'evolving' in time.
Each "g" takes-in two variables and returns the product of these variables with an outside global variable indexed at the same time-step.
Assume obs_mat (T x 1) is a pre-defined global array, and t corresponds to the time-steps
G = []
for t in range(T):
# tried declaring obs here too.
def g(current_state, observation_noise):
obs = obs_mat[t]
return current_state * observation_noise * obs
G.append(g)
Unfortunately when I test the resultant functions, they do not seem to pick up on the difference in the obs time-varying constant i.e. (Got G[0](100,100) same as G[5](100,100)). I tried playing around with the scope of obs but without much luck. Would anyone be able to help guide me in the right direction?
This is a common "gotcha" to referencing variables from an outer scope when in an inner function. The outer variable is looked up when the inner function is run, not when the inner function is defined (so all versions of the function see the variable's last value). For each function to see a different value, you either need to make sure they're looking in separate namespaces, or you need to bind the value to a default parameter of the inner function.
Here's an approach that uses an extra namespace:
def make_func(x):
def func(a, b):
return a*b*x
return func
list_of_funcs = [make_func(i) for i in range(10)]
Each inner function func has access to the x parameter in the enclosing make_func function. Since they're all created by separate calls to make_func, they each see separate namespaces with different x values.
Here's the other approach that uses a default argument (with functions created by a lambda expression):
list_of_funcs = [lambda a, b, x=i: a*b*x for i in range(10)]
In this version, the i variable from the list comprehension is bound to the default value of the x parameter in the lambda expression. This binding means that the functions wont care about the value of i changing later on. The downside to this solution is that any code that accidentally calls one of the functions with three arguments instead of two may work without an exception (perhaps with odd results).
The problem you are running into is one of scoping. Function bodies aren't evaluated until the fuction is actually called, so the functions you have there will use whatever is the current value of the variable within their scope at time of evaluation (which means they'll have the same t if you call them all after the for-loop has ended)
In order to see the value that you would like, you'd need to immediately call the function and save the result.
I'm not really sure why you're using an array of functions. Perhaps what you're trying to do is map a partial function across the time series, something like the following?
from functools import partial
def g(current_state, observation_noise, t):
obs = obs_mat[t]
return current_state * observation_noise * obs
g_maker = partial(g, current, observation)
results = list(map(g_maker, range(T)))
What's happening here is that partial creates a partially-applied function, which is merely waiting for its final value to be evaluated. That final value is dynamic (but the first two are fixed in this example), so mapping that partially-applied function over a range of values gets you answers for each value.
Honestly, this is a guess because it's hard to see what else you are trying to do with this data and it's hard to see what you're trying to achieve with the array of functions (and there are certainly other ways to do this).
The issue (assuming that your G.append call is mis-indented) is simply that the name t is mutated when you loop over the iterator returned by range(T). Since every function g you create stores returns the same name t, they wind up all returning the same value, T - 1. The fix is to de-reference the name (the simplest way to do this is by sending t into your function as a default value for an argument in g's argument list):
G = []
for t in range(T):
def g(current_state, observation_noise, t_kw=t):
obs = obs_mat[t_kw]
return current_state * observation_noise * obs
G.append(g)
This works because it creates another name that points at the value that t references during that iteration of the loop (you could still use t rather than t_kw and it would still just work because tg is bound to the value that tf is bound to - the value never changes, but tf is bound to another value on the next iteration, while tg still points at the "original" value.

Resources