在函數內部,能夠調用其餘函數。若是一個函數在內部調用自身自己,這個函數就是遞歸函數。數據結構
舉個例子,咱們來計算階乘 n! = 1 * 2 * 3 * ... * n,用函數 fact(n)表示,能夠看出:ide
fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n
因此,fact(n)能夠表示爲 n * fact(n-1),只有n=1時須要特殊處理。函數
因而,fact(n)用遞歸的方式寫出來就是:code
def fact(n): if n==1: return 1 return n * fact(n - 1)
上面就是一個遞歸函數。能夠試試:htm
>>> fact(1) 1 >>> fact(5) 120 >>> fact(100) 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
若是咱們計算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
遞歸函數的優勢是定義簡單,邏輯清晰。理論上,全部的遞歸函數均可以寫成循環的方式,但循環的邏輯不如遞歸清晰。ip
使用遞歸函數須要注意防止棧溢出。在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出。能夠試試計算 fact(10000)。it
漢諾塔 (http://baike.baidu.com/view/191666.htm) 的移動也能夠看作是遞歸函數。io
咱們對柱子編號爲a, b, c,將全部圓盤從a移到c能夠描述爲:class
若是a只有一個圓盤,能夠直接移動到c;
若是a有N個圓盤,能夠當作a有1個圓盤(底盤) + (N-1)個圓盤,首先須要把 (N-1) 個圓盤移動到 b,而後,將 a的最後一個圓盤移動到c,再將b的(N-1)個圓盤移動到c。
請編寫一個函數,給定輸入 n, a, b, c,打印出移動的步驟:
move(n, a, b, c)
例如,輸入 move(2, 'A', 'B', 'C'),打印出:
A --> B
A --> C
B --> C
函數 move(n, a, b, c) 的定義是將 n 個圓盤從 a 藉助 b 移動到 c。
參考代碼:
def move(n, a, b, c): if n ==1: print a, '-->', c return move(n-1, a, c, b) print a, '-->', c move(n-1, b, a, c) move(4, 'A', 'B', 'C')