Recursive vs Iterative Functions Python - python

I am currently learning Python and would like some clarification on the difference between iterative and recursive functions. I understand that recursive functions call themselves but I am not exactly sure how to define an iterative function.
For instance, I wrote this code
random_list = ['6', 'hello', '10', 'find', '7']
def sum_digits(string):
return sum(int(x) for x in string if x.isdigit())
print "Digits:", sum_digits(random_list)
I thought that this was an iterative function but after doing some research I am not sure. I need to know specifically because the next exercise asks me to write a version of the function that is recursive/iterative (depending on what my first function is).

Recursive function call itself while does not reach the out poin whereas iterative function update calculating value through the iteration over the range.

so the question is "write an iterative and recursive version of sum". great.
don't use the built-in sum method, and write your own. i'll give you the iterative, you should figure out the recursive:
def my_iterative_sum(a):
res = 0
for n in a:
res += a
return res
this is iterative because it iterates over all the values and sums them up.
edit: clearly your post is iterative. are you calling function f from within function f? no.
maybe reading up on what recursion is will help with this. https://www.google.com/search?q=recursion

To those who might still want to see the difference between recursive and iterative function.
iterative
def iterative_sum(n):
result = 1
for i in range(2,n+1):
result *= i
return result
print(iterative_sum(5))
iteration is when a loop repeatedly executes until the controlling condition becomes false
recursive
def recursive_sum(n):
if n == 1:
return 1
else:
return n * recursive_sum(n-1)
print(recursive_sum(5))
recursive function is when a function calls itself
This link explains it much better
https://techdifferences.com/difference-between-recursion-and-iteration-2.html

In case you came here looking for recursive functions in Python, here is an example of a recursive sum
from typing import Optional
>>> def r_sum(v: int, sum: Optional[int] = None) -> int:
... if sum is None:
... sum = 0
... if v == 0:
... return sum
... else:
... sum = sum + v
... prev = v - 1
... return r_sum(prev, sum)
...
>>> r_sum(3)
6
>>> r_sum(4)
10
>>> r_sum(5)
15

Related

Intermediate results from recursion

I have a problem where I need to produce something which is naturally computed recursively, but where I also need to be able to interrogate the intermediate steps in the recursion if needed.
I know I can do this by passing and mutating a list or similar structure. However, this looks ugly to me and I'm sure there must be a neater way, e.g. using generators. What I would ideally love to be able to do is something like:
intermediate_results = [f(x) for x in range(T)]
final_result = intermediate_results[T-1]
in an efficient way. While my solution is not performance critical, I can't justify the massive amount of redundant effort in that first line. It looks to me like a generator would be perfect for this except for the fact that f is fundamentally much more suited to recursion in my case (which at least in my mind is the complete opposite of a generator, but maybe I'm just not thinking far enough outside of the box).
Is there a neat Pythonic way of doing something like this that I just don't know about, or do I just need to just capitulate and pollute my function f by passing it an intermediate_results list which I then mutate as a side-effect?
I have a generic solution for you using a decorator. We create a Memoize class which stores the results of previous times the function is executed (including in recursive calls). If the arguments given have already been seen, the cached versions are used to quickly lookup the result.
The custom class has the benefit over an lru_cache in that you can see the results.
from functools import wraps
class Memoize:
def __init__(self):
self.store = {}
def save(self, fun):
#wraps(fun)
def wrapper(*args):
if args not in self.store:
self.store[args] = fun(*args)
return self.store[args]
return wrapper
m = Memoize()
#m.save
def fibo(n):
if n <= 0: return 0
elif n == 1: return 1
else: return fibo(n-1) + fibo(n-2)
Then after running different things you can see what the cache contains. When you run future function calls, m.store will be used as a lookup so calculation doesn't need to be redone.
>>> f(8)
21
>>> m.store
{(1,): 1,
(0,): 0,
(2,): 1,
(3,): 2,
(4,): 3,
(5,): 5,
(6,): 8,
(7,): 13,
(8,): 21}
You could modify the save function to use the name of the function and the args as the key, so that multiple function results can be stored in the same Memoize class.
You can use your existing solution that makes many "redundant" calls to f, but employ the use of function caching to save the results to previous calls to f.
In other words, when f(x1) is called, it's input arguments and corresponding return values are saved, and the next time it is called, the result is simply pulled from the cache
see functools.lru_cache for the standard library solution to this
ie:
from functools import lru_cache
#lru_cache
intermediate_results = [f(x) for x in range(T)]
final_result = intermediate_results[T-1]
Note, however, f must be a pure function (no side-effects, 1-to-1 mapping) for this to work properly
Having considered your comments, I'll now try to give another perspective on the problem.
So, let's consider a concrete example:
def f(x):
a = 2
return g(x) + a if x != 0 else 0
def g(x):
b = 1
return h(x) - b
def h(x):
c = 1/2
return f(x-1)*(1+c)
I
First of all, it should be mentioned that (in our particular case) the algorithm has form of: f(x) = p(f(x - 1)) for some p. It follows that f(x) = p^x(f(0)) = p^x(0). That means we should just apply p to 0 x times to get the desired result, which can be done in an iterative process, so this can be written without recursion. Though I believe that your real case is much harder. Moreover, it would be too boring and uninformative to stop here)
II
Generally speaking, we can divide all possible solutions into two groups: the ones that require refactoring (i.e. rewriting functions f, g, h) and the ones that do not. I have little to offer from the latter one (and I don't think anyone can). Consider the following, however:
def fk(x, k):
a = 2
return k(gk(x, k) + a if x != 0 else 0)
def gk(x, k):
b = 1
return k(hk(x, k) - b)
def hk(x, k):
c = 1/2
return k(fk(x-1, k)*(1+c))
def printret(x):
print(x)
return x
f(4, printret) # see what happens
Inspired by continuation-passing style, but that's totally not it.
What's the point? It's something between your idea of passing a list to write down all the computations and memoizing. This k carries additional behavior with it, such as printing or writing to list (you can make a function that writes to some list, why not?). But if you look carefully you'll see that it lefts inner code of these functions practically untouched (only input and output to function are affected), so one can produce a decorator associated with a function like printret that does essentially the same thing for f, g, h.
Pros: no need to modify code, much more flexible than passing a list, no additional work (like in memoizing).
Cons: Impure (printing or modifying sth), not so flexible as we would like.
III
Now let's see how modifying function bodies can help. Don't be afraid of what's written below, take your time and play with that thing a little.
class Logger:
def __init__(self, lst, cur_val):
self.lst = lst
self.cur_val = cur_val
def bind(self, f):
res = f(self.cur_val)
return Logger([self.cur_val] + res.lst + self.lst, res.cur_val)
def __repr__(self):
return "Logger( " + repr({'value' : self.cur_val,'lst' : self.lst}) + " )"
def unit(x):
return Logger([], x)
# you can also play with lala
def lala(x):
if x <= 0:
return unit(1)
else:
return lala(x - 1).bind(lambda y: unit(2*y))
def f(x):
a = 2
if x == 0:
return unit(0)
else:
return g(x).bind(lambda y: unit(y + a))
def g(x):
b = 1
return h(x).bind(lambda y: unit(y - b))
def h(x):
c = 1/2
return f(x-1).bind(lambda y: unit(y*(1+c)))
f(4) # see for yourself
Logger is called a monad. I'm not very familiar with this concept myself, but I guess I'm doing everything right) f, g, h are functions that take a number and return a Logger instance. Logger's bind takes in a function (like f) and returns Logger with new value (computed by f) and updated 'logs'. The key point - as I see it - is the ability to do whatever we want with collected functions in the order the resulting value was calculated.
Afterword
I'm not at all some kind of 'guru' of functional programming, I believe I'm missing a lot of things here. But what I've understood is that functional programming is about inversing the flow of the program. That's why, for instance, I totally agree with your opinion about generators being opposed to functional programming. When we use generator gen in, say, function func, we yield values one by one to func and func does sth with them in e.g. a loop. The functional approach would be to make gen a function taking func as a parameter and make func perform computations on 'yielded' values. It's like gen and func exchanged their places. So the flow is inversed! And there are plenty of other ways of inversing the flow. Monads are one of them.
itertools islice gets a generator, start value and stop value. it will give you the elements between the start value and stop value as a generator. if islice is not clear you can check the docs here https://docs.python.org/3/library/itertools.html
intermediate_result = map(f, range(T))
final_result = next(itertools.islice(intermediate_result, start=T-1, stop=T))

Python Lambda usage

Im trying to learn how to utilize the lambda function and i am having some difficulties.
example from HomeWork.
i have the following functions
def square(x):
return x**2
def incr(x):
return x+1
i need to create a function called 'repeated' that will take one of the above functions + an integer and will return some value that depends on the times the functions have been called IE:
repeated (incr,4)(2)
6
repeated (square,2)(5)
625
using a 'compose' function.
def compose(f,g):
return lambda x:f(g(x))
thank in advance for an explanation.
If you should use a lambda, you can use recursion like this:
def repeated(func, times):
return lambda x: repeated(func, times - 1)(func(x)) if times > 0 else x
what repeated return is a function waiting for an argument, x,and then it activates itself times times (descriptive...) on x
When it should no longer activate itself (in other words, times <= 0, it stops and returns the x the last call received.
You can use compose in the same manner as follows:
If you want to use compose you can do this:
def repeated(func, times):
return compose(func, repeated(func, times-1)) if times > 0 else lambda x
What this does is just call compose recursively times - 1 more times, using repeated, until no more composing is needed.
Edit:
Python's standard implementation and recursion don't get along too well, both of these solution are correct, but using a loop is more pythonic. Another answer is available that does just that.
repeated needs to return a callable too; compose() can produce that callable for you, but you need to apply it multiple times:
def repeated(f, count):
callable = f
for _ in range(count - 1):
callable = compose(f, callable)
return callable
So if count is 1, the original f() is returned. For count = 2, f(f()) is returned, etc.

calculating current value based on previous value

i would like to perform a calculation using python, where the current value (i) of the equation is based on the previous value of the equation (i-1), which is really easy to do in a spreadsheet but i would rather learn to code it
i have noticed that there is loads of information on finding the previous value from a list, but i don't have a list i need to create it! my equation is shown below.
h=(2*b)-h[i-1]
can anyone give me tell me a method to do this ?
i tried this sort of thing, but that will not work as when i try to do the equation i'm calling a value i haven't created yet, if i set h=0 then i get an error that i am out of index range
i = 1
for i in range(1, len(b)):
h=[]
h=(2*b)-h[i-1]
x+=1
h = [b[0]]
for val in b[1:]:
h.append(2 * val - h[-1]) # As you add to h, you keep up with its tail
for large b list (brr, one-letter identifier), to avoid creating large slice
from itertools import islice # For big list it will keep code less wasteful
for val in islice(b, 1, None):
....
As pointed out by #pad, you simply need to handle the base case of receiving the first sample.
However, your equation makes no use of i other than to retrieve the previous result. It's looking more like a running filter than something which needs to maintain a list of past values (with an array which might never stop growing).
If that is the case, and you only ever want the most recent value,then you might want to go with a generator instead.
def gen():
def eqn(b):
eqn.h = 2*b - eqn.h
return eqn.h
eqn.h = 0
return eqn
And then use thus
>>> f = gen()
>>> f(2)
4
>>> f(3)
2
>>> f(2)
0
>>>
The same effect could be acheived with a true generator using yield and send.
First of, do you need all the intermediate values? That is, do you want a list h from 0 to i? Or do you just want h[i]?
If you just need the i-th value you could us recursion:
def get_h(i):
if i>0:
return (2*b) - get_h(i-1)
else:
return h_0
But be aware that this will not work for large i, as it will exceed the maximum recursion depth. (Thanks for pointing this out kdopen) In that case a simple for-loop or a generator is better.
Even better is to use a (mathematically) closed form of the equation (for your example that is possible, it might not be in other cases):
def get_h(i):
if i%2 == 0:
return h_0
else:
return (2*b)-h_0
In both cases h_0 is the initial value that you start out with.
h = []
for i in range(len(b)):
if i>0:
h.append(2*b - h[i-1])
else:
# handle i=0 case here
You are successively applying a function (equation) to the result of a previous application of that function - the process needs a seed to start it. Your result looks like this [seed, f(seed), f(f(seed)), f(f(f(seed)), ...]. This concept is function composition. You can create a generalized function that will do this for any sequence of functions, in Python functions are first class objects and can be passed around just like any other object. If you need to preserve the intermediate results use a generator.
def composition(functions, x):
""" yields f(x), f(f(x)), f(f(f(x)) ....
for each f in functions
functions is an iterable of callables taking one argument
"""
for f in functions:
x = f(x)
yield x
Your specs require a seed and a constant,
seed = 0
b = 10
The equation/function,
def f(x, b = b):
return 2*b - x
f is applied b times.
functions = [f]*b
Usage
print list(composition(functions, seed))
If the intermediate results are not needed composition can be redefined as
def composition(functions, x):
""" Returns f(x), g(f(x)), h(g(f(x)) ....
for each function in functions
functions is an iterable of callables taking one argument
"""
for f in functions:
x = f(x)
return x
print composition(functions, seed)
Or more generally, with no limitations on call signature:
def compose(funcs):
'''Return a callable composed of successive application of functions
funcs is an iterable producing callables
for [f, g, h] returns f(g(h(*args, **kwargs)))
'''
def outer(f, g):
def inner(*args, **kwargs):
return f(g(*args, **kwargs))
return inner
return reduce(outer, funcs)
def plus2(x):
return x + 2
def times2(x):
return x * 2
def mod16(x):
return x % 16
funcs = (mod16, plus2, times2)
eq = compose(funcs) # mod16(plus2(times2(x)))
print eq(15)
While the process definition appears to be recursive, I resisted the temptation so I could stay out of maximum recursion depth hades.
I got curious, searched SO for function composition and, of course, there are numerous relavent Q&A's.

Python calling a function inside a function

I'm a beginner to CS, and I've been trying to work through a Python book (Think Python) on my own.
I'm currently on recursion, but I'm a bit stuck.
The exercise asks for me to write a function called do_n that takes a function object and a number, n, as arguments, and that calls the given function n times.
This is my code:
def countdown(n):
if n<= 0:
print 'Blastoff'
return
else:
print n
countdown(n-1)
def do_n(f(n), x):
if x<=0:
return
else:
f(n)
do_n(f, x-1)
do_n(countdown(3), 3)
When I do this, there's an error because of invalid syntax in def do_n(f(n), x). If I change it to:
def do_n(f, x):
if x<=0:
return
else:
f(n)
do_n(f, x-1)
There is an error because n is not defined in the else statement.
How can I make this work?
You are almost there with your second example. It takes a function f and a max count x. n doesn't exist because you haven't written the loop to generate values for n yet. Turns out python has a builtin for that
def do_n(f, x):
for n in range(x):
f(n)
Now do_n takes a function object f and a count x, then calls the function count times. Notice the difference between f (the function object) and f(n) (the result of calling f with the value n). When you want to call do_n, you do it like this:
do_n(countdown, 3)
Not like
do_n(countdown(3), 3) # badness
That last one calls countdown and then calls do_n with its result.
def print_n(s,n):
if n<=0:
return
else:
print(s)
print_n(s,n-1)
def do_n(f,s,n,x):
if x<=0:
return
else:
f(s,n)
do_n(f,s,n,x-1)
do_n(print_n,'Hello',2,2)
It's a tricky one.
Basically, the solution is:
def do_n(f, n):
if n <= 0:
return
f(n)
do_n(f, n-1)
However, if you actually try combining it with print_n then all hell breaks loose.
First of all, we have to add one more argument, s, to the above code to be able to pass any string that we want to print out n times.
Second of all, the above do_n should be able to repeat any passed function n times so long as the function doesn't mess with do_n. Unfortunately, this is exactly what happens here.
The assignment from the book seems clear: "...write a function called do_n that takes a function object and a number, n, as arguments, and that calls the given function n times..." or in other words do_n calls any function passed to it n times. You'd think if we pass s = 'Hello' and n = 3 then do_n(f, s, n) should output 'Hello' 9 times... because print_n('Hello', 3) prints 'Hello' 3 times and do_n(print_n, 'Hello', 3) is supposed to triple that result... gotcha.
What actually happens is in the first instance of recursion do_n does indeed print out 'Hello' 3 times, because print_n says so. But in the next instance do_n prints out 'Hello' only two times, because it has subtracted 3-1 in the previous instance and now print_n('Hello', 2). In the last instance do_n prints 'Hello' only once, for the same reason. So the total amount of 'Hello' output is 6.
Finally, if you use FOR LOOP instead of recursion to define do_n, you will actually get the correct result of 9 outputs:
def print_n(s, n):
if n <= 0:
return
print(s)
print_n(s, n-1)
def do_n(f, s, n):
for i in range(n):
f(s, n)
do_n(print_n, 'Hello', 3)
P.S.
On Page 52 of Think Python 2E we are encouraged to use recursion over for loop:
"For simple examples like this, it is probably easier to use a for loop. But we will see examples later that are hard to write with a for loop and easy to write with recursion, so it is good to start early."
However, in this particular exercise, using recursion forces an output which clashes with the assignment, seeing that do_n and print_n are at odds with each other because constructing do_n with the help of recursion wrongly reduces n in each instance.
Did I get it right?
thinkpython2e
recursion

python: one line function returning a generator

I want to write the function below in a more concise manner:
def sum_list(l):
x = 0
for i in l:
x += i
return x
I know I could easily do this:
def sum_list(l):
return sum(l)
But I have been tinkering with generators and list comprehension in an effort to better understand python.
So I tried:
def sum_list(l):
x = 0
return (x += i for i in l)
But this just returns the generator object. I remember reading somewhere that it should be used within an operation like sum() or something along those line but I cannot seem to find that article anymore.
Can someone kindly point me in the direction of some literature that covers this, or possibly take the time to explain some of the basics surrounding statements of this nature?
I think you want reduce - http://docs.python.org/library/functions.html#reduce
def sum_iterable(a):
return reduce(lambda x,y: x+y, a, 0)
Basically, reduce lets you 'fold' an iterable or list into a value. You wouldn't want a generator expression for something that should return a single value.

Categories