I am currently making a program that includes a recursive function that always returns 1. The function looks like this:
def fn(n):
if n <= 1:
return n
elif n > 1 and n % 2 == 0:
return fn(n/2)
elif n > 1 and n % 2 > 0:
return fn(3*n+1)
As I will be creating other functions, I need to create a function that counts how many times fn() is called recursively within the fn() function (How many calls does 'n' take to reach 1). I am able to do this using a global variable, however I am not sure how to do this using another recursive function. Any help would be appreciated. Thanks.
Why not add a second parameter to your function and increment that on recursive calls?
def fn(n):
def _fn(n, calls):
if n <= 1:
return n, calls
# n > 1 is a given by this point.
return _fn(n / 2 if n % 2 == 0 else 3 * n + 1, calls + 1)
return _fn(n, 1)
Use cProfile
foo.py:
def fn(n):
if n <= 1:
return n
elif n > 1 and n % 2 == 0:
return fn(n/2)
elif n > 1 and n % 2 > 0:
return fn(3*n+1)
if __name__ == "__main__":
import sys
fn(int(sys.argv[1]))
Then execute with:
python -m cProfile foo.py 10
10 function calls (4 primitive calls) in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 foo.py:1(<module>)
7/1 0.000 0.000 0.000 0.000 foo.py:1(fn)
1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
This output indicates that fn()
is called seven times.
A solution is to add an optional parameter to your function which you can increment at each function's call.
def fn(n, ncalls=1):
if n <= 1:
return n, ncalls
elif n > 1 and n % 2 == 0:
return fn(n/2, ncalls + 1)
elif n > 1 and n % 2 > 0:
return fn(3*n+1, ncalls + 1)
FWIW an arguably simpler option is to hold the count state inside the function itself, without having to nest it nor wrap it in a decorator.
This is akin to doing it thru a global variable, but with the added benefit of restricting the count to the function scope.
For instance:
def fn(n):
try:
fn.count += 1
except AttributeError:
fn.count = 1
if n <= 1:
return n
elif n > 1 and n % 2 == 0:
return fn(n/2)
elif n > 1 and n % 2 > 0:
return fn(3*n+1)
Output:
In [15]: fn(5)
Out[15]: 1.0
In [16]: fn.count
Out[16]: 6
PS: Your n > 1
check is unnecessary. You might simplify your function a bit by dropping it altogether:
def fn(n):
try:
fn.count += 1
except AttributeError:
fn.count = 1
if n <= 1:
return n
return fn((3*n + 1) if n % 2 else (n / 2))
Maybe a decorator might be the solution here:
def count(func):
def counted(value):
counted.call_count += 1
return func(value)
counted.call_count = 0
return counted
Now, the code would look like
@count
def fn(n):
if n <= 1:
return n
elif n > 1 and n % 2 == 0:
return fn(n/2)
elif n > 1 and n % 2 > 0:
return fn(3*n+1)
(which is just the code but with an extra @count
)
fn(n)
would return 1 or 1.0, and fn.call_count
would return the number of calls.
The simply way:
ncalls=0
def fn(n):
global ncalls
ncalls +=1
if n <= 1:
return n
elif n > 1 and n % 2 == 0:
return fn(n/2)
elif n > 1 and n % 2 > 0:
return fn(3*n+1)
if __name__ == "__main__":
n = 10
print(f'fn({n}) = {fn(n)}, {ncalls} function call(s)')
This topic is not so obvious unless you can decorate your function (fully replacing the reference to a function in its definition scope).
If you can't , you better define a function for use with Fixed Point operator , like below:
In [21]: import itertools, functools
...: def fn(recurse, n):
...: if n <= 1:
...: return n
...: elif n > 1 and n % 2 == 0:
...: return recurse(n/2)
...: elif n > 1 and n % 2 > 0:
...: return recurse(3*n+1)
...:
...:
...: def fix(f, *args, **kwargs):
...: def g(*args, **kwargs):
...: return f(g, *args, **kwargs)
...: return g
...:
...: def count_calls(fn, *args, **kwargs):
...: cnt = itertools.count()
...: def wrapper(recurse, *args, **kwargs):
...: cnt.__next__()
...: return fn(recurse, *args, **kwargs)
...: fix(wrapper)(*args, **kwargs)
...: return next(cnt)
...:
...:
...: print(fix(fn)(10))
...: count_calls(fn, 10)
1.0
Out[21]: 7
BTW such representation of a recursion allows you to control calls stack or even to translate recursive calls to a loops, great invention:)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.