1. 程式人生 > >測開之函式進階· 第1篇《遞迴函式》

測開之函式進階· 第1篇《遞迴函式》

## 堅持原創輸出,點選藍字關注我吧 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220162312.png) 作者:清菡 部落格:oschina、雲+社群、知乎等各大平臺都有。 > 由於微信公眾號推送改為了資訊流的形式,防止走丟,請給加個星標 ⭐,你就可以第一時間接收到本公眾號的推送! # 目錄 - 一、往期回顧 - 1.生成器程式碼詳解 - 2.生成器的三個方法 - 二、遞迴函式 - 1.什麼是遞迴函式 - 2.遞迴函式呼叫原理圖 - 3.遞迴邊界 - 4.通過遞迴函式實現的任意數的階乘 - 5.這個遞迴函式的遞迴臨界點在哪? - 6.斐波那契數列 - 三、系列推薦 ## 一、往期回顧 ### 1.生成器程式碼詳解 ```PYTHON def gen(): for i in range(5): j = yield i print(j) # send:與生成器進行互動 g = gen() print(next(g)) print(next(g)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220113415.png) 第一個`print(next(g))`列印的 0,就是生成器生成的元素。第二個`print(next(g))`列印的 1 也是生成器生成的元素,None 是`print(j)`列印的`j`。 通過生成器獲取元素的時候,首先生成器進去的話,當呼叫生成器獲取裡面的值,它會從上往下走,走到`j = yield i`這裡,把`yield`這裡的`i`這個值返回出來,呼叫完`gen()`返回一個生成器`g`。 通過這個生成器`next(g)`去拿值的時候,然後它從上往下執行程式碼,走到`j = yield i`這裡,`yield`相當於把`i`,通過`yield`返回出去。 從生成器裡面返回出來,就生成一個數據。生成這個`i`,到第一個`print(next(g))`這裡,列印的就是`i`。 第二個`print(next(g))`,再用`next()`呼叫生成器的時候,那麼這個生成器會從`yield`之後繼續往下執行。 通過`next()`去觸發生成器的時候,`yield`之後是沒有內容的,`j`接收的就是空的,所以列印`j`的時候,打印出來的是個`None`。 ### 2.生成器的三個方法 ```PYTHON # 生成器的三個方法: send close throw def gen(): for i in range(5): j = yield i print(j) # send:與生成器進行互動 g = gen() print(g.send(100)) print(next(g)) print(next(g)) ``` 執行後報錯: ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220115752.png) 生成器的`send()`方法,它執行的時候會從上一個`yield`結束的地方來進行執行。 在這裡只建立了`gen()`這個生成器,這個生成器還沒有生成過任何資料,這個時候生成器就暫停在函式最開始的地方`def gen():`這裡。 這裡`send(100)`這個值進去的話,在這裡執行,直接執行`for i in range(5):`這個語句,`send(100)`生成進去的這個值沒有地方接收,所以報錯了。 **`send()`必須在呼叫了一次`next()`之後才呼叫。可以和`next()`一樣,去獲取生成器裡面的內容。** #### 2.1`next()`獲取生成器裡面的內容: ```PYTHON # 生成器的三個方法: send close throw def gen(): for i in range(5): j = yield i print(j) # send:與生成器進行互動 g = gen() print(next(g)) print(next(g)) # print(g.send(100)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220121125.png) #### 2.2`send()`在呼叫了一次`next()`之後呼叫,獲取生成器裡面的內容: ```PYTHON # 生成器的三個方法: send close throw def gen(): for i in range(5): j = yield i print(j) # send:與生成器進行互動 g = gen() print(next(g)) print(g.send(100)) # print(next(g)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220120838.png) **`yield`只能在函式裡面用。**`yield`關鍵字是用在建立生成器的時候,只要函式裡面使用了`yield`關鍵字,在呼叫函式的時候,函式不會立馬被執行。 因為這個函式不是簡單的函數了,它是個生成器。 在函式外面,是沒辦法用`yield`關鍵字的。 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220121948.png) #### 2.3`close()`:關閉生成器 ```PYTHON def gen(): for i in range(5): j = yield i print(j) yield 100 # send:與生成器進行互動 g = gen() print(next(g)) # print(next(g)) # print(g.send(100)) # close:關閉生成器 g.close() print(next(g)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220124019.png) #### 2.4`throw()`方法:在生成器內部主動引發一個異常。引數:1.異常型別。2.異常資訊。 這個方法可以接收 2 個引數,第一個引數:`Exception` 異常型別。第二個引數:傳入異常的資訊。 Exception 報錯: `g.throw(Exception,"Method throw called!")` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220125434.png) ValueError: `g.throw(ValueError,"清菡,大事不好,報錯了,嚶嚶嚶~")` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220125556.png) ## 二、遞迴函式 ### 1.什麼是遞迴函式 **在函式中呼叫函式自身,我們把這樣的函式叫做遞迴函式。** ### 2.遞迴函式呼叫原理圖 ![遞迴函式呼叫自身的圖,圖片來自網路](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220154308.png) ### 3.遞迴邊界 遞迴邊界:**退出遞迴的終止條件。** ```PYTHON def func(): print('99999') func() func() ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220132818.png) 在外面呼叫函式,直接陷入一個死迴圈。在函式內部呼叫`func()`這個函式,又到`def func():`這裡來執行,然後`print('99999')`,又`func()`呼叫。 不斷得自身呼叫,這樣就造成了死迴圈。 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220133811.png) **Pycharm 有個檢測機制:** 當它內部檢測到這個是個無限遞迴,沒有遞迴臨界點的一個遞迴函式,那麼這個時候,它遞迴多少次之後,會自動給終止了。 使用遞迴函式的時候,一定要注意一個點:**就是一定要設定遞迴的邊界。遞迴的邊界就是遞迴函式的終止條件。** 如果你不設定遞迴邊界,那麼你定義的遞迴函式就是個死迴圈,一直無限得呼叫自身。 ### 4.通過遞迴函式實現的任意數的階乘 #### 4.1 什麼是階乘? | 1 的階乘 | 1 | | -------- | -------------- | | 2 的階乘 | 1 * 2 | | 3 的階乘 | 1 * 2 * 3 | | 4 的階乘 | 1 * 2 * 3 * 4 | 遞迴能實現的,通過迴圈都能實現。 Python 中遞迴用得不多,不太建議使用遞迴,因為遞迴不太好用,用遞迴還不如用迴圈。 #### 4.2 怎麼去算階乘呢? 定義個函式,算任意數的階乘。傳 1,就算 1 的階乘,傳 10 就算 10 的階乘。 **可以這樣做:** 首先要判斷下它傳進來的這個引數是不是等於 1,如果是等於 1 的話,就直接給它`return`返回出去。然後,如果它不等於 1 的話,就返回`return n * (n-1)*(n-2)`。 n 傳進來是 1,那應該返回 1;如果傳的是 2,應該返回`return n * (n-1)`。 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220141705.png) 如果在這裡用遞迴函式,呼叫`func(1)`。那麼這個時候,這個`func(1)`呼叫遞迴函式。 `這個函式返回的是什麼?` 呼叫這段程式碼: ```PYTHON if n == 1: return 1 ``` 返回的是個 1。 將程式碼修改成如下: ```python def fun(n): if n == 1: return 1 else: return n * fun(n-1) fun(3) ``` 如果是`fun(3)`,3 傳進來: ```PYTHON def fun(n): if n == 1: return 1 ``` 肯定是不成立的。 `else`後面的程式碼`return n * fun(n-1)`。 這裡的 n 是個 3,`fun(n-1)`就是`fun(2)`,那麼就是`3 * fun(2)`。 這個時候會再次呼叫自身這個函式: ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201220143534.png) `這個時候 n 是什麼?` `fun(2)`的時候 n 是個 2,就是`3 *2* fun(1)`。 `fun(1)`再執行下,出來的結果是個 1。那這裡就是個 1,就是`3*2*1`。 等於 3 的時候,返回的結果就是`3*2*1`。 #### 4.3 改成`fun(4)`看看: 首先 4 進來,n 等於 4,`fun(n-1)`就是`fun(3)`。呼叫`fun(3)`就相當於再次呼叫`fun(n)`,就是`4 *3* fun(2)`。 再次呼叫`fun(2)`,再進來,前面`return n * fun(n-1)`這一截得到 2,`fun(3-1)`得到 2,所以最終得到`4*3*2* fun(1)`。 `fun(1)`呼叫,結果出來就是個 1。就是`4*3*2*1`。 ```PYTHON def fun(n): if n == 1: return 1 else: return n * fun(n-1) # 4 *3*2*1 fun(4) ``` ### 5.這個遞迴函式的遞迴臨界點在哪? ```PYTHON if n == 1: return 1 ``` 當`n=1`的時候就不會呼叫自身了。當滿足某個條件,不再呼叫自身,那麼這個就被稱為遞迴臨界點。 例如改成`n==-1` ```PYTHON if n == -1: return 1 ``` `這個時候,這個函式的遞迴臨界點在哪?` 這個遞迴臨界點就是`-1`。 ```python def fun(n): if n == -1:# 遞迴臨界點:當達到遞迴臨界點的時候,就不再呼叫自身函式的條件 return 1 else: return n * fun(n-1) # 4 *3*2*1 fun(4) ``` 任何遞迴函式,它的原理都是一樣的。定義一個遞迴函式,在遞迴函式裡面它其實就是不斷得呼叫自身,然後設定遞迴函式的時候,一定不能忘了遞迴條件。 ### 6.斐波那契數列 **後面的數都是等於前 2 個數相加的結果。** 斐波那契數列的第一個數值是 1,第二個數值也是個 1,第三個數等於前兩個數相加的結果(那就是 2),第四個數等於於前兩個數相加的結果(那就是 3)。 `[1,1,2,3,5]` 以此類推。 ## 三、系列推薦 - [測開入門篇《環境管理、編碼規範、專案結構》](https://mp.weixin.qq.com/s/aXKYIZ8K4f7dWOfCQCDBXg) - [資料型別· 第 1 篇《元組和列表的效能分析、命名元組》](https://mp.weixin.qq.com/s/6bKgDRYONbM9V8vUxCH0ug) - [資料型別第 2 篇「字典和集合的原理和應用」](https://mp.weixin.qq.com/s/baCQFwWwR8KbXkFYBsLaJA) - [測開之資料型別· 第 3 篇《列表推導式、字典推導式、2 種方式建立生成器》](https://mp.weixin.qq.com/s/McgUcQxjujBILR9QtzJ95w) - [測開之資料型別· 第 4 篇《迭代器、生成器》](https://mp.weixin.qq.com/s/drShVcnZJQRMoOPF_ReS2g) --- 公眾號 **「清菡軟體測試」** 首發,更多原創文章:**清菡軟體測試 110+原創文章**,歡迎關注、交流,禁止第三方擅自轉載。