我目前正在制作一个程序,其中包含一个始终返回 1 的递归函数。 该函数如下所示:
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)
因为我将创建其他函数,所以我需要创建一个函数来计算 fn() 函数内递归调用 fn() 的次数(“n”需要多少次调用才能达到 1)。我可以使用全局变量来做到这一点,但是我不确定如何使用另一个递归函数来做到这一点。任何帮助,将不胜感激。谢谢。
答案 0 :(得分:2)
为什么不在您的函数中添加第二个参数并在递归调用时增加它?
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)
答案 1 :(得分:1)
使用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]))
然后执行:
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}
此输出表明 fn()
被调用了七次。
答案 2 :(得分:1)
一个解决方案是向您的函数添加一个可选参数,您可以在每个函数调用时增加该参数。
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)
答案 3 :(得分:1)
FWIW 一个可以说更简单的选择是将计数状态保存在函数本身内部,而不必将其嵌套或包装在装饰器中。
这类似于通过全局变量执行此操作,但具有将计数限制在函数范围内的额外好处。
例如:
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)
输出:
In [15]: fn(5)
Out[15]: 1.0
In [16]: fn.count
Out[16]: 6
PS:您的 n > 1
检查是不必要的。您可以通过完全删除它来简化您的功能:
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))
答案 4 :(得分:1)
也许装饰器可能是这里的解决方案:
def count(func):
def counted(value):
counted.call_count += 1
return func(value)
counted.call_count = 0
return counted
现在,代码看起来像
@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)
(这只是代码,但带有额外的 @count
)
fn(n)
将返回 1 或 1.0,而 fn.call_count
将返回调用次数。
答案 5 :(得分:0)
简单的方法:
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)')
答案 6 :(得分:0)
除非您可以修饰您的函数(在其定义范围内完全替换对函数的引用),否则这个主题并不那么明显。
如果不能,您最好定义一个与 Fixed Point operator 一起使用的函数,如下所示:
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
顺便说一句,递归的这种表示允许您控制调用堆栈,甚至将递归调用转换为循环,伟大的发明:)