1. 程式人生 > >Python遞迴函式如何寫?正確的Python遞迴函式用法

Python遞迴函式如何寫?正確的Python遞迴函式用法

前言
本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯絡我們以作處理。

在函式內部,可以呼叫其他函式。如果一個函式在內部呼叫自身本身,這個函式就是遞迴函式。如果你還對遞迴還不懂或者python學的不是很好,建議去小編的Python交流.裙 :一久武其而而流一思(數字的諧音)轉換下可以找到了,裡面有最新Python教程專案可拿,多跟裡面的人交流,進步更快哦!

一、舉個例子,我們來計算階乘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時需要特殊處理。
於是,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
二、遞迴函式的優點是定義簡單,邏輯清晰。理論上,所有的遞迴函式都可以寫成迴圈的方式,但迴圈的邏輯不如遞迴清晰。
使用遞迴函式需要注意防止棧溢位。在計算機中,函式呼叫是通過棧(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
尾遞迴呼叫時,如果做了優化,棧不會增長,因此,無論多少次呼叫也不會導致棧溢位。

遺憾的是,大多數程式語言沒有針對尾遞迴做優化,Python直譯器也沒有做優化,所以,即使把上面的fact(n)函式改成尾遞迴方式,也會導致棧溢位。

小結
使用遞迴函式的優點是邏輯簡單清晰,缺點是過深的呼叫會導致棧溢位。

針對尾遞迴優化的語言可以通過尾遞迴防止棧溢位。尾遞迴事實上和迴圈是等價的,沒有迴圈語句的程式語言只能通過尾遞迴實現迴圈。

Python標準的直譯器沒有針對尾遞迴做優化,任何遞迴函式都存在棧溢位的問題。
練習
漢諾塔的移動可以用遞迴函式非常簡單地實現。

請編寫move(n, a, b, c)函式,它接收引數n,表示3個柱子A、B、C中第1個柱子A的盤子數量,然後打印出把所有盤子從A藉助B移動到C的方法,例如:

-- coding: utf-8 --

def move(n, a, b, c):

if n == 1:
    print(a, '-->', c)

期待輸出:

A --> C

A --> B

C --> B

A --> C

B --> A

B --> C

A --> C

move(3, ‘A’, ‘B’, ‘C’)
Run
希望對大家有幫助!