Python3萌新入門筆記(21)

這一篇教程的學習目標是瞭解什麼是遞歸(Recursion)。

簡單來說,遞歸就是函數自己調用自己。(聽起來…好淫蕩…)

但是,自己調用自己不會變成無限循環調用麼?

例如下面這個代碼:

def recursion():
    return recursion()

recursion()

代碼運行後會拋出異常,RecursionError: maximum recursion depth exceeded

意思是,遞歸錯誤:超過最大遞歸深度

也就是說,因爲函數不停的循環調用自身超過了一定次數導致的異常。

這種叫無窮遞歸(Infinite Recursion),一般來說並沒有什麼用。

我們需要有用的遞歸。

比較經典的應用有階乘運算、冪運算和二分查詢(看到這些詞語別暈,實際上很簡單)。

我們先來看階乘運算。

階乘:一個正整數的階乘是所有小於及等於該數的正整數的積

舉例來說,5的階乘就是5*4*3*2*1,4的階乘就是4*3*2*1。

其實,階乘的運算如果不使用遞歸我們也可以實現。

示例代碼:

def factorial(n):
    result = n 
    for i in range(1, n): 
        result *= i
    return result

print(factorial(5)) # 顯示輸出結果爲:120

然後,我們來看使用遞歸方式的實現。

示例代碼:

def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1) # 函數中調用函數自身

print(factorial(5)) # 顯示輸出結果爲:120

遞歸方式實現的階乘,好像有些不太容易理解。

我們把計算過程中,參數變量的值通過print語句顯示出來看一下。

修改後的代碼:

def factorial(n):
    print(n)  # 顯示輸出參數值
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1)

factorial(5)

當我們運行代碼,得到如下結果:

這意味着print語句被執行了5次,同時也意味着函數被調用了5次。

先記下這個特點,我們繼續通過print語句把每次通過return語句計算的結果也顯示出來。

修改後代碼如下:

def factorial(n):
    if n == 1:
        print('1')  # 顯示輸出當前的階乘結果
        return 1
    else:
        current = n * factorial(n - 1)
        print(current)  # 顯示輸出當前的階乘結果
        return current

factorial(5)

當我們運行代碼,得到如下結果:

這個結果意味着,每次調用函數都會進行一次計算,並且將計算結果通過return語句返回。

但是,遞歸的具體執行過程仍然很模糊。

我們來看一個示意圖。

上方5個色塊,是代碼調用了5此函數。

每次調用函數都會創建新的命名空間,大家可以理解爲程序執行了5個同名的函數。

既然執行了5個函數,就有參數傳入和返回結果的過程。

我們先來看調用函數時,參數傳入的過程。

  • 函數首次調用時,參數n的值爲5;
  • 首次調用函數的return語句中,進行了第二次調用函數,並設置參數爲n-1;所以,在第二次調用的函數中,參數n的值變成了4;
  • 以此類推,直至終止調用函數自身爲止。

接下來,我們再來看返回函數執行結果的過程。

  • 程序調用5次函數的同時,進行了參數的傳入,第5次調用時,參數n的值是1,;此時,參數數值滿足n == 1的條件,不再繼續調用函數自身,通過return語句返回值,也就是1;
  • 當1這個值被返回,程序回到了倒數第2次函數調用的return語句,此時語句中對函數的最後一次調用變成了具體的值(1),和變量n相乘之後,作爲返回值,再次返回給倒數第3次函數調用的return語句中;
  • 以此類推,直至返回到首次調用的函數爲止。

所以,在我們剛纔的運行結果截圖中,我們能夠到,參數的顯示結果是5、4、3、2、1的順序(依次調用函數的順序);而返回值的顯示結果是1、2、6、24、120的順序(從最後一次調用函數向前返回計算結果的順序)。

所以,遞歸可以這麼理解,它就是函數循環調用函數自身;遞是指在循環調用的過程中,將參數進下一次函數的調用;歸是指當滿足終止循環調用函數的條件時,按照和調用時相反的順序,將函數的執行結果依次迴歸。

接下來,我們再來看一下冪的計算。

例如,2的4次方實際上是2*2*2*2的乘積。

示例代碼:

def power(x, y):  # 定義函數計算參數x的y次方
    if y == 1:  # 滿足條件時終止函數循環調用
        return x  # 參數x的1次方返回參數x
    else:
        return x * power(x, y - 1)  # 調用函數自身形成遞歸

print(power(2, 4))  # 顯示輸出結果爲:16

上方代碼中,當我們調用函數power時,函數開始循環調用函數自身,並且依次將參數y(3,2,1)傳入每次調用的函數中,當參數y爲1的時候,結束函數自身的循環調用,並且依次返回值(2,4,8,16)。

最後,我們再來看一下二分查詢的案例。

例如,有一個0~100的數字列表,從中查詢到一個指定的數字。

示例代碼:

def search(seq, number, lower, upper):  # 定義函數,參數seq爲要查詢的序列,參數number爲要查詢的數字
    # 參數lower爲查找區間的下限值,參數upper爲查找區間的上限值
    if lower == upper:  # 查找區間僅剩一個數字時,即找到查詢結果,停止循環調用
        assert number != seq[upper], '這裏有問題!'  # 如果不相等會拋出異常AssertionError(斷言錯誤),可以嘗試把"=="改爲"!="
        print(str(lower))  # 顯示輸出結果爲:66
    else:  # 查找區間有多個數字時,繼續查找
        middle = (lower + upper) // 2  # 通過整除計算,獲取查找區間數字的中間值
        if number > seq[middle]:  # 判斷查找的數字是否大於中間值
            return search(seq, number, middle + 1, upper)  # 如果大於中間值,中間值作爲查找區間下限值,繼續查找
        else:
            return search(seq, number, lower, middle)  # 如果小於中間值,中間值作爲查找區間上限值,繼續查找


search(range(0, 100), 66, 0, 100)  # 顯示輸出結果爲:66

大家根據代碼中的註釋對遞歸過程進行理解,在此不再贅述。

不過在上方代碼中,我添加了一個assert關鍵字,此關鍵字爲斷言,可以幫助我們運行程序檢查語句中的錯誤。

使用格式爲:assert 表達式, ‘自定義的異常說明字符串’

自定義的異常說明字符串可以省略,未省略時,字符串和表達式之間必須用逗號(,)分隔。

如果斷言語句中的表達式出現錯誤,則會拋出AssertionError異常。

如果自定義了異常說明字符串,則會顯示“AssertionError:自定義的異常說明字符串”。

本節知識點:

1、函數的遞歸;

2、斷言的使用。

本節英文單詞與中文釋義:

1、recursion:遞歸

2、Infinite:無窮

3、maximum:最大值

4、depth:深度

5、exceeded:超過

6、factorial:階乘

7、search:查詢

8、power:冪

9、lower:下方

10、upper:上方

11、middle:中間

12、assert/assertion:異常

練習:根據給定的姓名,在列表中查詢姓名的序號(0-68)。

lst=[‘邢佳棟’,’李學慶’,’高昊’,’潘粵明’,’戴軍’,’薛之謙’,’賈宏聲’,’于波’,’李連杰’,’王斑’,’藍雨’,’劉恩佑’,’任泉’,’李光潔’,’姜文’,’黑龍’,’張殿菲’,’鄧超’,’張傑’,’楊坤’,’沙溢’,’李茂’,’黃磊’,’于小偉’,’劉冠翔’,’秦俊傑’,’張琳’,’陳坤’,’黃覺’,’邵峯’,’陳旭’,’馬天宇’,’楊子’,’鄧安奇’,’趙鴻飛’,’馬可’,’黃海波’,’黃志忠’,’李晨’,’後弦’,’王挺’,’何炅’,’朱亞文’,’胡軍’,’許亞軍’,’張涵予’,’賈乃亮’,’陸虎’,’印小天’,’於和偉’,’田亮’,’夏雨’,’李亞鵬’,’胡兵’,’王睿’,’保劍鋒’,’於震’,’甦醒’,’胡夏’,’張豐毅’,’劉翔’,’李玉剛’,’林依輪’,’袁弘’,’朱雨辰’,’丁志誠’,’黃徵’,’張子健’,’許嵩’]

答案:(見原文評論1樓)

轉載自:魔力 • Python » Python3萌新入門筆記(21)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章