python-遞歸函數

#recursion_function.py
#-*- coding:utf-8 -*-編程

def recursion(x):
    if x == 1:
        return 1
    return x * recursion(x - 1)
    
f1 = recursion(1)
f2 = recursion(2)
f3 = recursion(3)
f4 = recursion(4)
print f1, f2, f3, f4數據結構

     遞歸函數的優勢是定義簡單,邏輯清晰。理論上,全部的遞歸函數均可以寫成循環的方式,但循環的邏輯不如遞歸清晰。編程語言

    使用遞歸函數須要注意防止棧溢出。在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出。函數

解決遞歸調用棧溢出的方法是經過尾遞歸優化,事實上尾遞歸和循環的效果是同樣的,因此,把循環當作是一種特殊的尾遞歸函數也是能夠的。優化

尾遞歸是指,在函數返回的時候,調用自身自己,而且,return語句不能包含表達式。這樣,編譯器或者解釋器就能夠把尾遞歸作優化,使遞歸自己不管調用多少次,都只佔用一個棧幀,不會出現棧溢出的狀況。spa

上面的fact(n)函數因爲return n * fact(n - 1)引入了乘法表達式,因此就不是尾遞歸了。要改爲尾遞歸方式,須要多一點代碼,主要是要把每一步的乘積傳入到遞歸函數中:code

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 - 1num * product在函數調用前就會被計算,不影響函數調用。遞歸

fact(5)對應的fact_iter(5, 1)的調用以下:utf-8

===> 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標準的解釋器沒有針對尾遞歸作優化,任何遞歸函數都存在棧溢出的問題。

 

Reference:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00137473836826348026db722d9435483fa38c137b7e685000

相關文章
相關標籤/搜索