华南俳烁实业有限公司

python

當(dāng)前位置:中華考試網(wǎng) >> python >> python編程基礎(chǔ) >> 文章內(nèi)容

初學(xué)者必看的python遞歸函數(shù)

來(lái)源:中華考試網(wǎng)  [2020年10月7日]  【

  初學(xué)者必看的Python遞歸函數(shù)

  在函數(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)用遞歸的方式寫(xiě)出來(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)933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536

  97920827223758251185210916864000000000000000000000000

  如果我們計(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ù)都可以寫(xiě)成循環(huán)的方式,但循環(huán)的邏輯不如遞歸清晰。

python學(xué)習(xí)課程預(yù)約提醒

  • 地區(qū):
  • 姓名:
  • 手機(jī):

  使用遞歸函數(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 "", line 1, in

  File "", line 4, in fact

  ...

  File "", line 4, in fact

  RuntimeError: maximum recursion depth exceeded in comparison

  解決遞歸調(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(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)僅返回遞歸函數(shù)本身,num - 1和num * product在函數(shù)調(diào)用前就會(huì)被計(jì)算,不影響函數(shù)調(diào)用。

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

  ===> fact_iter(5, 1)

  ===> fact_iter(4, 5)

  ===> fact_iter(3, 20)

  ===> fact_iter(2, 60)

  ===> fact_iter(1, 120)

  ===> 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)致棧溢出。

  小結(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)題。

責(zé)編:hym
  • 會(huì)計(jì)考試
  • 建筑工程
  • 職業(yè)資格
  • 醫(yī)藥考試
  • 外語(yǔ)考試
  • 學(xué)歷考試
德钦县| 景洪市| 平阴县| 兰州市| 辰溪县| 青冈县| 巴青县| 祁阳县| 康乐县| 安新县| 绥阳县| 平山县| 梧州市| 聊城市| 方山县| 大足县| 建昌县| 枞阳县| 宣化县| 丽水市| 舞阳县| 康平县| 泰和县| 日照市| 安西县| 莎车县| 璧山县| 青川县| 乌恰县| 航空| 嘉善县| 牟定县| 湖北省| 枝江市| 绥芬河市| 夏邑县| 深水埗区| 兴化市| 云阳县| 襄垣县| 永泰县|