[英]Counting how many times a function is called recursively (Python)
我目前正在制作一個程序,其中包含一個總是返回 1 的遞歸 function。function 看起來像這樣:
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)
由於我將創建其他函數,因此我需要創建一個 function 來計算在 fn() function 中遞歸調用 fn() 的次數(“n”需要多少次調用才能達到 1)。 我可以使用全局變量來做到這一點,但是我不確定如何使用另一個遞歸 function 來做到這一點。 任何幫助,將不勝感激。 謝謝。
為什么不向您的 function 添加第二個參數並在遞歸調用中增加它呢?
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)
使用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}
這個 output 表示fn()
被調用了七次。
一種解決方案是向您的 function 添加一個可選參數,您可以在每個函數調用時遞增該參數。
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 一個可以說是更簡單的選擇是將計數 state 保存在 function 本身內部,而不必嵌套它或將其包裝在裝飾器中。
這類似於通過全局變量執行此操作,但具有將計數限制為 function scope 的額外好處。
例如:
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:您的n > 1
檢查是不必要的。 您可以通過完全刪除 function 來稍微簡化它:
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))
也許裝飾器可能是這里的解決方案:
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
將返回調用次數。
簡單的方法:
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)')
除非您可以裝飾您的 function(在其定義范圍內完全替換對 function 的引用),否則這個主題並不是那么明顯。
如果你不能,你最好定義一個 function 用於定點運算符,如下所示:
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
順便說一句,這種遞歸表示允許您控制調用堆棧,甚至可以將遞歸調用轉換為循環,偉大的發明:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.