欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

講解Python中的遞歸函數(shù)

 更新時(shí)間:2015年04月27日 10:26:28   作者:廖雪峰  
這篇文章主要介紹了講解Python中的遞歸函數(shù),遞歸是學(xué)一門編程語(yǔ)言必須掌握的重要特性,需要的朋友可以參考下

在函數(shù)內(nèi)部,可以調(diào)用其他函數(shù)。如果一個(gè)函數(shù)在內(nèi)部調(diào)用自身本身,這個(gè)函數(shù)就是遞歸函數(shù)。

舉個(gè)例子,我們來(lái)計(jì)算階乘n! = 1 x 2 x 3 x ... x n,用函數(shù)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時(shí)需要特殊處理。

于是,fact(n)用遞歸的方式寫出來(lái)就是:

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ì)算過(guò)程如下:

===> 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)用是通過(guò)棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用,棧就會(huì)加一層棧幀,每當(dāng)函數(shù)返回,棧就會(huì)減一層棧幀。由于棧的大小不是無(wú)限的,所以,遞歸調(diào)用的次數(shù)過(guò)多,會(huì)導(dǎo)致棧溢出??梢栽囋噁act(1000):

>>> fact(1000)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<stdin>", line 4, in fact
 ...
 File "<stdin>", line 4, in fact
RuntimeError: maximum recursion depth exceeded

解決遞歸調(diào)用棧溢出的方法是通過(guò)尾遞歸優(yōu)化,事實(shí)上尾遞歸和循環(huán)的效果是一樣的,所以,把循環(huán)看成是一種特殊的尾遞歸函數(shù)也是可以的。

尾遞歸是指,在函數(shù)返回的時(shí)候,調(diào)用自身本身,并且,return語(yǔ)句不能包含表達(dá)式。這樣,編譯器或者解釋器就可以把尾遞歸做優(yōu)化,使遞歸本身無(wú)論調(diào)用多少次,都只占用一個(gè)棧幀,不會(huì)出現(xiàn)棧溢出的情況。

上面的fact(n)函數(shù)由于return n * fact(n - 1)引入了乘法表達(dá)式,所以就不是尾遞歸了。要改成尾遞歸方式,需要多一點(diǎn)代碼,主要是要把每一步的乘積傳入到遞歸函數(shù)中:

def fact(n):
  return fact_iter(1, 1, n)

def fact_iter(product, count, max):
  if count > max:
    return product
  return fact_iter(product * count, count + 1, max)

可以看到,return fact_iter(product * count, count + 1, max)僅返回遞歸函數(shù)本身,product * count和count + 1在函數(shù)調(diào)用前就會(huì)被計(jì)算,不影響函數(shù)調(diào)用。

fact(5)對(duì)應(yīng)的fact_iter(1, 1, 5)的調(diào)用如下:

===> fact_iter(1, 1, 5)
===> fact_iter(1, 2, 5)
===> fact_iter(2, 3, 5)
===> fact_iter(6, 4, 5)
===> fact_iter(24, 5, 5)
===> fact_iter(120, 6, 5)
===> 120

尾遞歸調(diào)用時(shí),如果做了優(yōu)化,棧不會(huì)增長(zhǎng),因此,無(wú)論多少次調(diào)用也不會(huì)導(dǎo)致棧溢出。

遺憾的是,大多數(shù)編程語(yǔ)言沒(méi)有針對(duì)尾遞歸做優(yōu)化,Python解釋器也沒(méi)有做優(yōu)化,所以,即使把上面的fact(n)函數(shù)改成尾遞歸方式,也會(huì)導(dǎo)致棧溢出。

有一個(gè)針對(duì)尾遞歸優(yōu)化的decorator,可以參考源碼:

http://code.activestate.com/recipes/474088-tail-call-optimization-decorator/

我們后面會(huì)講到如何編寫decorator?,F(xiàn)在,只需要使用這個(gè)@tail_call_optimized,就可以順利計(jì)算出fact(1000):

>>> fact(1000)
402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

小結(jié)

使用遞歸函數(shù)的優(yōu)點(diǎn)是邏輯簡(jiǎn)單清晰,缺點(diǎn)是過(guò)深的調(diào)用會(huì)導(dǎo)致棧溢出。

針對(duì)尾遞歸優(yōu)化的語(yǔ)言可以通過(guò)尾遞歸防止棧溢出。尾遞歸事實(shí)上和循環(huán)是等價(jià)的,沒(méi)有循環(huán)語(yǔ)句的編程語(yǔ)言只能通過(guò)尾遞歸實(shí)現(xiàn)循環(huán)。

Python標(biāo)準(zhǔn)的解釋器沒(méi)有針對(duì)尾遞歸做優(yōu)化,任何遞歸函數(shù)都存在棧溢出的問(wèn)題。

相關(guān)文章

  • Python數(shù)據(jù)讀寫之Python讀寫CSV文件

    Python數(shù)據(jù)讀寫之Python讀寫CSV文件

    這篇文章主要介紹了Python數(shù)據(jù)讀寫之Python讀寫CSV文件,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-06-06
  • PyQt5利用QPainter繪制各種圖形的實(shí)例

    PyQt5利用QPainter繪制各種圖形的實(shí)例

    下面小編就為大家?guī)?lái)一篇PyQt5利用QPainter繪制各種圖形的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Python高級(jí)排序sort()函數(shù)使用技巧實(shí)例探索

    Python高級(jí)排序sort()函數(shù)使用技巧實(shí)例探索

    本文詳細(xì)介紹sort()函數(shù)的使用,包括基本排序、自定義排序、逆序排序等多種情況,并提供大量示例代碼,以幫助你充分理解和掌握這一函數(shù)的用法,探索更多sort()排序函數(shù)的作用
    2024-01-01
  • python中threading開啟關(guān)閉線程操作

    python中threading開啟關(guān)閉線程操作

    這篇文章主要介紹了python中threading開啟關(guān)閉線程操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-05-05
  • 詳解Python的字符串格式化

    詳解Python的字符串格式化

    這篇文章主要介紹了Python的字符串格式化,python的format函數(shù)怎么用,這篇文章向大家介紹format函數(shù)用法,需要的朋友可以參考下
    2023-04-04
  • 如何把外網(wǎng)python虛擬環(huán)境遷移到內(nèi)網(wǎng)

    如何把外網(wǎng)python虛擬環(huán)境遷移到內(nèi)網(wǎng)

    這篇文章主要介紹了如何把外網(wǎng)python虛擬環(huán)境遷移到內(nèi)網(wǎng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • jupyter notebook tensorflow打印device信息實(shí)例

    jupyter notebook tensorflow打印device信息實(shí)例

    這篇文章主要介紹了jupyter notebook tensorflow打印device信息實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-04-04
  • 實(shí)現(xiàn)python?namedtuple元類編程

    實(shí)現(xiàn)python?namedtuple元類編程

    這篇文章主要為大家介紹了實(shí)現(xiàn)python?namedtuple元類編程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 人生苦短我用python python如何快速入門?

    人生苦短我用python python如何快速入門?

    這篇文章主要教大家如何快速入門python,一個(gè)簡(jiǎn)短而全面的入門教程帶你走入Python的大門,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 詳細(xì)解讀Python字符串的使用與f-string

    詳細(xì)解讀Python字符串的使用與f-string

    這篇文章主要介紹了詳細(xì)解讀Python字符串的使用與f-string,在?Python?中,引號(hào)內(nèi)的任何內(nèi)容都是字符串,但是字符串也有很多的用法,需要的朋友一起來(lái)看看吧
    2023-04-04

最新評(píng)論