python 學習彙總36:遞迴函式(尾遞迴)( tcy)
阿新 • • 發佈:2018-11-19
遞迴函式(尾遞迴) 2018/11/15
用途:
遞迴函式常用於檢索大量資料,替代for迴圈。
1.遞迴深度設定:
sys.getrecursionlimit() #返回當前最多的遞迴深度預設3000 #ipython
sys.setrecursionlimit(1000) #設定遞迴深度
注意: 不能用在生成器函式和協程中 函數語言程式設計並未通過 while 或者 for 宣告提供迭代功能,同樣也未提供狀態更新。 任何可迭代的程式碼都可以轉換為可遞迴的程式碼
2.例項1:定義階乘函式factorial ;遞迴實現
def factorial(n):
if n<=1:
return 1
else:
return n * factorial(n-1)
例項2:定義階乘factorial lambda表示式;遞迴實現
factorial1=(lambda n: 1 if n <= 1 else n * factorial1(n-1))#執行時間稍快於上面
測試 :
sys.setrecursionlimit(3000)#若設定3000,下面n<=2999 a=factorial(1) #1 print(a) a=factorial(5) #120 print(a) a=factorial(6) #720 print(a)
例項3:定義斐波那契數;遞迴實現
def fibonacci(n):
if n==1 or n==2:
return 1
return fibonacci(n-1)+fibonacci(n-2)
例項4:定義斐波那契數;lambda遞迴實現
fibonacci = (lambda x , x1=1,x2=0:
x2 if x == 0 else fibonacci (x - 1, x1 + x2, x1))
測試 :
a=fibonacci(1) #1 print(a) a=fibonacci(5) #5 print(a) a=fibonacci(6) #8 print(a)
3.尾遞迴
尾遞迴基於函式的尾呼叫, 每一級呼叫直接返回函式的返回值更新呼叫棧,而不用建立新的呼叫棧,
類似迭代的實現,時間和空間上均優化了一般遞迴!
python不支援尾遞迴。解決辦法先定義一個修飾符,然後在定義尾遞迴函式。
參考:
http://www.open-open.com/lib/view/open1480494663229.html
尾遞迴定義例項:
階乘例項函式修改為支援尾遞迴,不需要在設定遞迴深度
如果無修飾符那麼n的能用資料範圍為2899(假設你設定遞迴深度為3000的話)
@tail_call_optimized #修飾符函式在下面定義
def factorial(n,acc=1):#階乘
if n==0:
return acc
else:
return n*factorial(n-1,n*acc)
測試:
def factorial(n):#普通階乘遞迴定義,用於測試比對
if n<=1:
return 1
else:
return n * factorial(n-1)
n=2999
for i in range(n):
if factorial(i)==factorial1(i):
# print('ok')
pass
else:
print('ng')
print('end')
4.備註:
@tail_call_optimized的定義 :
4.1.異常類定義
class TailRecurseException(Exception):
def __init__(self, args, kwargs):
self.args = args
self.kwargs = kwargs
4.2.尾遞迴修飾符函式 :
def tail_call_optimized(g):
"""
這個函式用尾呼叫優化來修飾函式。它通過丟擲一個異常(如果是它自己的祖父母)來實現這一點,
並捕獲這樣的異常來偽造尾呼叫優化。如果修飾函式在非尾上下文中遞迴,則此函式將失敗。
"""
def func(*args, **kwargs):
''''
函式預設第一層遞迴是父呼叫,對於尾遞迴, 不希望產生新的函式呼叫(即:祖父呼叫),
所以這裡丟擲異常, 拿到引數, 退出被修飾函式的遞迴呼叫棧
'''
f = sys._getframe()
if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code:
raise TailRecurseException(args, kwargs)# 丟擲異常
else:
while 1:
try:
return g(*args, **kwargs)
except TailRecurseException as e:
args = e.args # 捕獲異常, 拿到引數, 退出被修飾函式的遞迴呼叫棧
kwargs = e.kwargs
func.__doc__ = g.__doc__
return func