Python筆記之遞迴函式

2021-08-29 02:41:53 字數 3696 閱讀 6202

python支援遞迴函式——即直接或者間接地呼叫自身以進行迴圈的函式。遞迴是python中比較的高階的話題,並且它在python中比較少見。然後,它是一項非常有用的技術,因為它允許程式遍歷擁有任意的,不可預知的形狀的結構。

我們來看乙個例子。假如要對乙個數字列表求和,我們可以使用內建的sum函式,或者是自己編寫乙個更加定製化的版本。示例1是用遞迴編寫的乙個定製求和函式:

#示例1

>>> def mysum(n):

... if not n:

... return 0

... else:

... return n[0] + mysum(n[1:])

...>>> mysum(range(10)) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

45

在每一層。這個函式都遞迴地呼叫自己來計算列表剩餘的值的和,這個和隨後加到前面的一項中。當列表為空的時候,遞迴迴圈結束並返回0.就像這樣使用遞迴的時候,對函式呼叫的每乙個開啟的層級,在執行時呼叫堆疊上都有自己的乙個函式本地作用域的副本,也就是說,這意味著n在每個層級都是不同的。

這可能對新手比較難以理解,我們可以這樣來看示例2。嘗試給函式新增乙個n的列印並再次執行它,從而在每個呼叫層級記錄下當前列表:

示例2

>>> def mysum(n):

... print(n)

... if not n:

... return 0

... else:

... return n[0] + mysum(n[1:])

...>>> mysum(list(range(10)))

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

[1, 2, 3, 4, 5, 6, 7, 8, 9]

[2, 3, 4, 5, 6, 7, 8, 9]

[3, 4, 5, 6, 7, 8, 9]

[4, 5, 6, 7, 8, 9]

[5, 6, 7, 8, 9]

[6, 7, 8, 9]

[7, 8, 9]

[8, 9][9]

45

正如你所看見的,在每個遞迴的層級上,要加和的列表變得越來越小,直到它變為空——遞迴迴圈結束。加和隨著遞迴呼叫的展開而計算開來。

這有乙個很有意思的是,我們可以使用python的三元if/else表示式在這裡儲存某些**。我們也可以針對任何可加和的型別一般化,

以下是一些例子:

示例3-1

>>> def mysum(n):

... return 0 if not n else n[0] + mysum(n[1:])

...>>> mysum(list(range(10)))

45示例3-2

>>> def mysum(n):

... return n[0] if len(n) == 1 else n[0] + mysum(n[1:])

...>>> mysum(list(range(10)))

45示例3-3

>>> def mysum(n):

... first, *rest = n

... return first if not rest else first + mysum(rest)

...>>> mysum(list(range(10)))

45#示例3-2和3-3會由於空的列表而失敗。

示例3-2和3-3會由於空的列表而失敗,但考慮到支援+任何物件型別的序列,而不單單是數字:

>>> mysum([1])

1>>> mysum(list(range(10)))

45>>> >>> mysum(('m', 'y', 'l', 'o', 'v', 'e', 'r'))

'mylover'

>>> mysum(['my', 'sweet', 'heart'])

'mysweetheart'

如果你比較好奇去研究這3個變體,將會發現,後2個在乙個單個字串引數上也有效(例如:mysum('love')),因為字串是一字元的字串的序列; 第三個變體中任意可迭代物件上都有效(包括開啟的檔案)。

前面我們說過遞迴可以是直接的,就像目前所給出的例子一樣,也可以是間接的,就像下面將要給出的例子一樣(乙個函式呼叫另乙個函式,後者反過來呼叫其呼叫者)。直接的效果是相同的,儘管這在每個層級有2個函式呼叫:

#示例4

>>> def mysum(n):

... if not n:

... return 0

... return nonempty(n)

...>>> def nonempty(n):

... return n[0] + mysum(n[1:])

...>>> mysum(list(range(10)))

45

儘管遞迴對於之前求和的例子都有效,但是在那種環境中,它可能有點過於追求技巧了。實際上,遞迴在python中並沒有像lisp那些語言中那樣常用,因為python強調像迴圈這樣的簡單的過程式語句,迴圈語句通常更為自然。例如,while常常使得事情變得更為具體一些,並且它不需要定義乙個支援遞迴呼叫的函式:

#示例5

>>> l = list(range(10))

>>> sum = 0

>>> while l:

... sum += l[0]

... l = l[1:]

...>>> sum

45

更好的情況是for迴圈為我們自動迭代,使得遞迴在大多數情況下不必使用(有可能,遞迴在記憶體空間和執行時間方面效率比較低):

>>> l = list(range(10))

>>> sum = 0

>>> for x in l:

... sum += x

...>>> sum

45

另一方面,遞迴可以要求遍歷任意形狀的結構(我們前面說過)。作為遞迴在這種環境中的應用的乙個簡單的例子,考慮像下面這樣的乙個任務:

#示例6

#計算乙個巢狀的子列表結構中的所有數字的總和

>>> def sumtree(n):

... tot = 0

... for x in n:

... if not isinstance(x, list):

... tot += x

... else:

... tot += sumtree(x)

... return tot

...>>> n = [2, [2, 5, [6, 3, [9]] , 6], 6, [5, 1]]

>>> print(sumtree(n))

45

簡單的迴圈語句在這裡不起作用了,因為這不是乙個線性迭代。巢狀的迴圈語句也不夠用,因為子列表可能巢狀到任意的深度並且以任意的形式巢狀。相反,示例6的**卻能夠使用遞迴來對應這種一般性的巢狀,以便順序訪問子列表。

筆記 python基礎之遞迴函式

學習要求 能看懂遞迴 能知道遞迴的應用場景 遞迴函式 了解什麼是遞迴 在函式中呼叫自身函式 最大遞迴深度預設是997 998 是python從記憶體角度出發做得限制 while true print 從前有座山 def story print 從前有座山 story print 111 story ...

Python筆記 遞迴函式

遞迴函式本質就是函式內部自己呼叫自己 遞迴函式最重要的就是找到出口 終止的條件 count 0 定義乙個全域性變數 deftell story global count count 1print 從前有座山 print 山上有座廟 print 廟裡有個老和尚 print 還有乙個小和尚 print ...

python函式之遞迴函式

在計算機程式設計裡,遞迴指的是乙個過程 函式不斷引用自身,直到引用的物件已知。1 自己呼叫自己 2 必須有乙個明確的結束條件 優點 缺點 計算n的階乘 def factorial n if n 1 return 1 fac n factorial n 1 return fac n 5print 的階...