I am writing a recursive function to make permutations of digits from 0
to n
. The program will return the th
permutation that is obtained. It all works well but I had to use the cheap trick of defining count
as a list, that is count=[0]
. In this way I am using the properties of lists in order to properly update the variable count[0]
at each iteration.
Ideally, what I would like to do is to define count
as an integer number instead. However, this does not work because count
is then updated only locally, within the scope of the function at the time it is called.
What is the proper way to count the iterations in a recursive function like this?
Below I show the code. It works, but I hate the way I am using count
here.
import numpy as np
N=10
available=np.ones(N)
def permutations(array, count=[0], n=N, start=0, end=N, th=100):
if count[0]<th:
for i in range(n):
if available[i]:
array[start]=i
if end-start>1:
available[i]=0
permutations(array, count, n, start 1, end)
available[i]=1
else:
count[0] =1
break
if count[0]==th:
a=''.join(str(i) for i in array)
return a
def main():
array=[0 for _ in range(N)]
count=[0]
print(permutations(array, count, N, start=0, end=N))
if __name__=="__main__":
main()
CodePudding user response:
Not necessarily ideal but to answer the question, one could use a global variable as follows:
import numpy as np
N = 10
available = np.ones(N)
count = 0
def permutations(array, n=N, start=0, end=N, th=100):
global count
if count < th:
for i in range(n):
if available[i]:
array[start] = i
if end-start > 1:
available[i] = 0
permutations(array, n, start 1, end)
available[i] = 1
else:
count = 1
break
if count == th:
return ''.join(str(i) for i in array)
def main():
array = [0 for _ in range(N)]
print(permutations(array, N, start=0, end=N))
print(f'{count=}')
if __name__ == "__main__":
main()
Output:
0123495786
count=100
CodePudding user response:
Different ways to update a variable from other scopes... and each with its own advantages and disadvantages (performance, access to the variable, ...):
with global
approach (as Pingu did),
with nonlocal
and with function's attribute.
The example under consideration, the factorial function, is merely illustrative but it can be easily readapted to your case.
def fact_global(n):
global counter
counter = 1
if n == 1:
return 1
return n*fact_global(n-1)
def fact_nonlocal(n):
counter = 0
def __fact(n):
nonlocal counter
counter = 1
if n == 1:
return 1
return n*__fact(n-1)
return __fact(n)
def fact_attr(n):
fact_attr.counter = 0
def __fact(n):
fact_attr.counter = 1
if n == 1:
return 1
return n*__fact(n-1)
return __fact(n)
n = 10
# case: global
counter = 0
fact_global(n)
print('global', counter)
# case: nonlocal
fact_nonlocal(n)
import inspect
fr = inspect.currentframe()
print('nonlocal', fr.f_locals['counter']) # not recommended, just for curiosity!
# case: function's attribute
fact_attr(n)
print('attr', fact_attr.counter)
Retrieving the value of the variable under investigation is quite straightforward with a global
-setting and with function's attribute but not trivial (and not recommended) with nonlocal
(inspect
is more a debugging tool).
Here a partial result of the workbench:
n=860
fact_nonlocal(n): 2.60644 ± 0.00586
fact_global(n): 2.74698 ± 0.02283
fact_attr(n): 3.01219 ± 0.00539
The results are of the same order of magnitude (due to limitations of the host only tested with a maximum of n=860
so not reliable for an asymptotic conclusion), in this case it seems that it doesn't really matter which one you choose but it is more important to focus on how you are going to access to the variable later in the program.