遞歸
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
使用遞歸函數(shù)需要注意防止棧溢出問題捅儒。在計算機中函數(shù)調用是通過棧(stack)這種數(shù)據(jù)結構實現(xiàn)的查刻,每當進入一個函數(shù)調用,棧就會增加一層棧幀秧饮,每當函數(shù)返回映挂,棧就會減一層棧幀。由于棧的大小不是無限的盗尸,所以柑船,遞歸調用的次數(shù)過多,會導致棧溢出泼各“笆保可以試試fact(1000)
:
>>>fack(1000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in fact
...
File "<stdin>", line 4, in fact
RuntimeError: maximum recursion depth exceeded(超過最大遞歸深度)
尾遞歸
解決遞歸調用棧溢出的方法是通過尾遞歸
優(yōu)化,事實上尾遞歸和循環(huán)的效果是一樣的,所以逆巍,把循環(huán)
看成是一種特殊的尾遞歸函數(shù)
也是可以的及塘。
尾遞歸是指,在函數(shù)返回的時候锐极,調用自身本身笙僚,并且,return語句不能包含表達式
灵再。這樣肋层,編譯器或者解釋器就可以把尾遞歸做優(yōu)化,使遞歸本身無論調用多少次檬嘀,都只占用一個棧幀槽驶,不會出現(xiàn)棧溢出的情況。
上面的fact(n)
函數(shù)由于return n * fact(n - 1)
引入了乘法表達式鸳兽,所以就不是尾遞歸了掂铐。要改成尾遞歸方式,需要多一點代碼揍异,主要是要把每一步的乘積傳入到遞歸函數(shù)中:
def fact(n):
return fact_iter(n, 1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
可以看到全陨,return fact_iter(num - 1, num * product)
僅返回遞歸函數(shù)本身,num - 1
和num * product
在函數(shù)調用前就會被計算衷掷,不影響函數(shù)調用辱姨。