在函數(shù)內(nèi)部经瓷,可以調(diào)用其他函數(shù)。如果一個(gè)函數(shù)在內(nèi)部調(diào)用自身本身茫经,這個(gè)函數(shù)就是遞歸函數(shù)。
舉個(gè)例子萎津,我們來計(jì)算階乘 n! = 1 * 2 * 3 * ... * n,用函數(shù) fact(n)表示抹镊,可以看出:
fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n
所以锉屈,fact(n)可以表示為 n * fact(n-1),只有n=1時(shí)需要特殊處理垮耳。
于是颈渊,fact(n)用遞歸的方式寫出來就是:
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
上面就是一個(gè)遞歸函數(shù)≈辗穑可以試試:
>>> fact(1)
1
>>> fact(5)
120
>>> fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
如果我們計(jì)算fact(5)俊嗽,可以根據(jù)函數(shù)定義看到計(jì)算過程如下:
===> 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
遞歸函數(shù)的優(yōu)點(diǎn)是定義簡(jiǎn)單,邏輯清晰铃彰。理論上绍豁,所有的遞歸函數(shù)都可以寫成循環(huán)的方式,但循環(huán)的邏輯不如遞歸清晰牙捉。
使用遞歸函數(shù)需要注意防止棧溢出竹揍。在計(jì)算機(jī)中,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的邪铲,每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用芬位,棧就會(huì)加一層棧幀,每當(dāng)函數(shù)返回带到,棧就會(huì)減一層棧幀昧碉。由于棧的大小不是無限的,所以揽惹,遞歸調(diào)用的次數(shù)過多被饿,會(huì)導(dǎo)致棧溢出∮浪浚可以試試計(jì)算 fact(10000)锹漱。
任務(wù)
漢諾塔 (http://baike.baidu.com/view/191666.htm) 的移動(dòng)也可以看做是遞歸函數(shù)。
我們對(duì)柱子編號(hào)為a, b, c慕嚷,將所有圓盤從a移到c可以描述為:
如果a只有一個(gè)圓盤哥牍,可以直接移動(dòng)到c毕泌;
如果a有N個(gè)圓盤,可以看成a有1個(gè)圓盤(底盤) + (N-1)個(gè)圓盤嗅辣,首先需要把 (N-1) 個(gè)圓盤移動(dòng)到 b撼泛,然后,將 a的最后一個(gè)圓盤移動(dòng)到c澡谭,再將b的(N-1)個(gè)圓盤移動(dòng)到c愿题。
請(qǐng)編寫一個(gè)函數(shù),給定輸入 n, a, b, c蛙奖,打印出移動(dòng)的步驟:
move(n, a, b, c)
例如潘酗,輸入 move(2, 'A', 'B', 'C'),打印出:
A --> B
A --> C
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')