在函數內部,能夠調用其餘函數。若是一個函數在內部調用自身自己,這個函數就是遞歸函數。
1、舉個例子,咱們來計算階乘n! = 1 x 2 x 3 x … x n,用函數fact(n)表示,能夠看出:
fact(n) = n! = 1 x 2 x 3 x … x (n-1) x n = (n-1)! x n = fact(n-1) x n
因此,fact(n)能夠表示爲n x fact(n-1),只有n=1時須要特殊處理。
ps:另外不少人在學習Python的過程當中,每每由於沒有好的教程或者沒人指導從而致使本身容易放棄,爲此我建了個Python交流.裙 :一久武其而而流一思(數字的諧音)轉換下能夠找到了,裏面有最新Python教程項目可拿,不懂的問題多跟裏面的人交流,都會解決哦!
因而,fact(n)用遞歸的方式寫出來就是:
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
上面就是一個遞歸函數。能夠試試:編程
fact(1)
1網絡fact(5)
120數據結構fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
若是咱們計算fact(5),能夠根據函數定義看到計算過程以下:
===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
2、遞歸函數的優勢是定義簡單,邏輯清晰。理論上,全部的遞歸函數均可以寫成循環的方式,但循環的邏輯不如遞歸清晰。
使用遞歸函數須要注意防止棧溢出。在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出。能夠試試fact(1000):編程語言fact(1000)
Traceback (most recent call last):
File 「」, line 1, in
File 「」, line 4, in fact
…
File 「」, line 4, in fact
RuntimeError: maximum recursion depth exceeded in comparison
解決遞歸調用棧溢出的方法是經過尾遞歸優化,事實上尾遞歸和循環的效果是同樣的,因此,把循環當作是一種特殊的尾遞歸函數也是能夠的。
尾遞歸是指,在函數返回的時候,調用自身自己,而且,return語句不能包含表達式。這樣,編譯器或者解釋器就能夠把尾遞歸作優化,使遞歸自己不管調用多少次,都只佔用一個棧幀,不會出現棧溢出的狀況。
上面的fact(n)函數因爲return n * fact(n - 1)引入了乘法表達式,因此就不是尾遞歸了。要改爲尾遞歸方式,須要多一點代碼,主要是要把每一步的乘積傳入到遞歸函數中:
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
尾遞歸調用時,若是作了優化,棧不會增加,所以,不管多少次調用也不會致使棧溢出。code
遺憾的是,大多數編程語言沒有針對尾遞歸作優化,Python解釋器也沒有作優化,因此,即便把上面的fact(n)函數改爲尾遞歸方式,也會致使棧溢出。blog
小結
使用遞歸函數的優勢是邏輯簡單清晰,缺點是過深的調用會致使棧溢出。教程
針對尾遞歸優化的語言能夠經過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能經過尾遞歸實現循環。
Python標準的解釋器沒有針對尾遞歸作優化,任何遞歸函數都存在棧溢出的問題。
練習
漢諾塔的移動能夠用遞歸函數很是簡單地實現。
請編寫move(n, a, b, c)函數,它接收參數n,表示3個柱子A、B、C中第1個柱子A的盤子數量,而後打印出把全部盤子從A藉助B移動到C的方法,例如:
def move(n, a, b, c):
if n == 1: print(a, '-->', c)
move(3, ‘A’, ‘B’, ‘C’)
Run
以上就是本次分享,另外不少人在學習Python的過程當中,每每由於沒有好的教程或者沒人指導從而致使本身容易放棄,爲此我建了個Python交流.裙 :一久武其而而流一思(數字的諧音)轉換下能夠找到了,裏面有最新Python教程項目可拿,不懂的問題多跟裏面的人交流,都會解決哦!
本文的文字及圖片來源於網絡加上本身的想法,僅供學習、交流使用,不具備任何商業用途,版權歸原做者全部,若有問題請及時聯繫咱們以做處理。