Reputation: 4592
My title might not be very direct, but basically, I want to change the following code written using for loop into a recursive function.
import numpy as np
irrel_pos = range(3, 11)
n_extra_pos = [2, 3]
extra_pos = []
rs = np.random.RandomState(1)
for i in range(len(n_extra_pos)):
sample_i = rs.choice(list(irrel_pos), n_extra_pos[i], replace=False)
extra_pos.append(set(sample_i))
irrel_pos = set(irrel_pos) - set(extra_pos[i])
print(irrel_pos)
print(extra_pos)
Output:
{8, 9, 3}
[{10, 5}, {4, 6, 7}]
Here is the code with two sample items from irrel_pos in the first loop and three items in the second loop, but the items sampled in the first loop need to be removed before going to the second loop.
I am trying to understand functional approach to this problem, how can I convert this to a recursive function?
Upvotes: 2
Views: 109
Reputation: 6891
Not dwelling on why you want to do this, there are at least two different approaches to this. It very much depends on whether you want the call to be a tail call (which allows for the compiler to optimize away the function call in many strict functional languages - though not Python) or if you want a more straight forward recursive function.
The straight forward implementation is
import numpy as np
def recursive(rs, n_extra_pos, irrel_pos):
if n_extra_pos == []:
return [], irrel_pos
extra_pos, irrel_pos = recursive(rs, n_extra_pos[:-1], irrel_pos)
sample = set(rs.choice(list(irrel_pos), n_extra_pos[-1], replace=False))
return extra_pos + [sample], irrel_pos - sample
irrel_pos = set(range(3, 11))
n_extra_pos = [2, 3]
rs = np.random.RandomState(1)
extra_pos, irrel_pos = recursive(rs, n_extra_pos, irrel_pos)
print(extra_pos, irrel_pos)
In this implementation, first we call ourself recursively until all elements of n_extra_pos
have been consumed, then while returning through the call stack, we do the needed calculations and update the result. As can be seen, the recursive call is not the last thing happening here, and therefore TCO (tail call optimization) is not possible.
To make a function that allows TCO, we need to use accumulators and instead of first reaching the bottom of the call stack, we will do the calculations on the way down, so to say, and while doing that, we will add the results to our accumulators (extra_pos
and irrel_pos
). Then, when reaching the bottom, all we have to do, is to return the accumulators. An implementation of this is
import numpy as np
def tail_recursive(rs, n_extra_pos, extra_pos, irrel_pos):
if n_extra_pos == []:
return extra_pos, irrel_pos
sample = set(rs.choice(list(irrel_pos), n_extra_pos[0], replace=False))
return tail_recursive(rs, n_extra_pos[1:],
extra_pos + [sample], irrel_pos - sample)
irrel_pos = set(range(3, 11))
n_extra_pos = [2, 3]
rs = np.random.RandomState(1)
extra_pos, irrel_pos = tail_recursive(rs, n_extra_pos, [], irrel_pos)
print(extra_pos, irrel_pos)
But, as stated, TCO is not available in Python, which makes recursive functions less useful and thereby makes Python less of an optimal choice for functional programming, other than using the built-in map
, reduce
(available in functools
) and filter
functions, and, of course, list comprehensions.
Upvotes: 4