##遞迴函式看的雲裡霧裡,廖雪峰大師給出的練習題我也沒搞懂,先mark下理解的,其他的後續再說……
通過之前的學習已經知道,函式的內部是可以呼叫其他函式的,如果乙個函式在內部呼叫自身本身,那麼這個函式就是遞迴函式。
定義乙個計算階乘n!的函式
n!= 1x2x3x4x……xn,用函式來表示可以看出
jx(n)= n! = 1 x 2 x 3 x ... x (n-1) x n = (n - 1)! x n = jx(n-1) x n
所以jx(n)可以表示為 jx(n-1) x n ,但是當n=1時,就不行了。因此需要額外處理一下n = 1 的情況。於是 階乘用遞迴方式寫出來就是:
def jx(n):
if n == 1:
return 1
return n * jx(n - 1)
遞迴函式定義簡單,邏輯清晰,理論上所有的遞迴函式都可以寫成迴圈的方式,但是迴圈的邏輯不如遞迴清晰。
棧溢位使用遞迴函式需要注意防止棧溢位, 函式呼叫是通過棧stack這種資料結構實現的,每當進入乙個函式呼叫,棧就會加一層棧幀,每當函式返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以當遞迴呼叫的次數過多時,就會導致棧溢位。
jx(1000)
recursionerror: maximum recursion depth exceeded in comparison
尾遞迴優化
解決遞迴呼叫棧溢位的方法是通過尾遞迴優化,事實上尾遞迴和迴圈的效果是一樣的,所以,把迴圈看成是一種特殊的尾遞迴函式也是可以的。
尾遞迴是指在函式返回的時候,呼叫自身本身,並且return語句不能包含表示式。這樣編譯器或者直譯器就可以把尾遞迴做優化,使遞迴無論呼叫多少次,都只占用乙個棧幀,不會出現棧溢位的情況。
例:在前面的例子裡,由於 return n * jx(n - 1) 引用了乘法表示式,所以就不是尾遞迴。要使用尾遞迴方式沒主要是要把每一步的乘積傳入到遞迴函式中。
def jx(n):
return jx_iter(n, 1)
def jx_iter(num,r1):
if num == 1:
return r1
return jx_iter(num - 1, num * r1) #返回本次計算結果給函式本身,進入下一輪迴圈。
尾遞迴呼叫時如果做了優化,棧不會增長,因此無論多少次呼叫也不會導致棧溢位。但是說這麼多,在試驗上面這個尾遞迴優化的例子時,發現python3依然會報錯,因為……python直譯器也沒做尾遞迴優化。
遞迴函式小結:
1. 使用遞迴函式的優點時邏輯簡單清晰,缺點是過深的呼叫會導致棧溢位。
2. 針對尾遞迴優化的語言可以通過尾遞迴防止棧溢位。尾遞迴事實上和迴圈是等價的,沒有迴圈語句的變成語言只能通過尾遞迴實現迴圈。
3. python標準的直譯器沒有針對尾遞迴做優化,任何遞迴函式都存在棧溢位的問題。
Day 6 函式式程式設計
函式式程式設計就是封裝成乙個個函式,一次呼叫來完成複雜任務。函式式程式設計的乙個特點是,允許把函式本身作為引數傳入另乙個函式,還允許返回乙個函式!高階函式就是將函式的變數名作為引數傳入,內部再對該函式進行呼叫的函式。乙個簡單的高階函式如下 def add x,y,f return f x f y x...
Day 6 函式與模組
def函式名 引數列表 函式體 示例1 計算矩形面積 函式名 area 引數 width 和 height 返回值 width height def area width,height 1 return width height 必須引數,按順序傳入引數 其中,5,6 按順序傳入,預設傳入width...
Day6 函式的作用域 引數 eval 遞迴
lambda表示式和匿名函式 eval 函式 遞迴函式 python中,一切都是物件 實際上,執行def定義函式後,系統就建立了相應的函式物件。def func1 print new pants func1 c func1 把func1的記憶體位址賦值給了c print func1,c c c 也可...