若是一個函數在內部調用自身自己,則該函數就是遞歸函數
遞歸優缺點
優勢:使用遞歸函數的優勢是邏輯簡單清晰
理論上,全部的遞歸函數均可以寫成循環的方式,但循環的邏輯不如遞歸清晰
缺點:過深的調用會致使棧溢出
棧溢出
使用遞歸函數須要注意防止棧溢出
在計算機中,函數調用是經過棧(stack)這種數據結構實現的
每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀
因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出
尾遞歸
解決遞歸調用棧溢出的方法是經過尾遞歸優化
事實上尾遞歸和循環的效果是同樣的,因此,把循環當作是一種特殊的尾遞歸函數也是能夠的編程
尾遞歸是指,在函數返回的時候,調用自身自己,而且return語句不能包含表達式
例如,def fun(n) : retrun n*fun(n-1), 則fun(n)只能等fun(n-1)結束才能夠,這樣一環套一環就會爆棧
在尾遞歸調用時,編譯器或者解釋器就能夠把尾遞歸作優化,使遞歸自己不管調用多少次,都只佔用一個棧幀,不會出現棧溢出的狀況
大多數編程語言沒有針對尾遞歸作優化,Python解釋器也沒有作優化,因此,即便把函數改爲尾遞歸方式,也會致使棧溢出
也就是說尾遞歸須要解釋器提供幫助,單純從代碼上是沒法完全實現的數據結構
針對尾遞歸優化的語言能夠經過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能經過尾遞歸實現循環
Python標準的解釋器沒有針對尾遞歸作優化,任何遞歸函數都存在棧溢出的問題編程語言
使用示例:函數
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)僅返回遞歸函數自己,num - 1和num * product在函數調用前就會被計算,不影響函數調用 #fact(5)對應的fact_iter(5, 1)的調用以下: ===> fact_iter(5, 1) ===> fact_iter(4, 5) ===> fact_iter(3, 20) ===> fact_iter(2, 60) ===> fact_iter(1, 120) ===> 120