1. 程式人生 > >決戰Python之巔(十一)

決戰Python之巔(十一)

前言

本篇將介紹遞迴以及函式的內建方法。

知識回顧

遞迴

之前我們已經講過,函式內部可以呼叫其他函式。如果一個函式在內部呼叫自身,這個函式就是遞迴函式:

def func()
	print('遞迴函式')
	func()

這就是一個遞迴函式,如果你執行這段程式碼的話,理想中會列印無數行 遞迴函式。但事實上並不會… 因為目前這個遞迴相當於一個死迴圈,一直執行下去很快會將記憶體撐爆,而Python為了防止這種情況發生就給遞迴次數做了一個限制,大概是1000次(這是預設次數,有需要你也可以自己手動修改。)
…其實到這裡遞迴就已經講完了- -
為了顯得專業一點,高階一點,實質上限制遞迴次數的原因是:在計算機中,函式呼叫是通過‘棧’這種資料結構實現的,每呼叫一次函式,棧就會加一層棧幀來儲存這層函式的資料;函式每return一次,棧中就減少一層。因為棧的大小是有限的,如果函式呼叫次數太多就會導致棧溢位。
這裡我們實現一個需求,階乘。階乘我們都學過,數學公式是: n! = n*(n-1)*(n-2)…*3*2*1
也就是:n! = n * (n-1)!,那麼用遞迴怎麼實現呢?

def  func(n):
	#判斷n是否為1,是則直接返回1
	if n == 1:
		return 1
	# n的階乘都等於n乘以(n-1)的階乘
	return n * func(n-1) 

假設n>1,第一次呼叫,進入第一層,先判斷n是否等於1,等於直接返回1(1的階乘就是1),不等於返回n*f(n-1),這時候第二次呼叫函式,進入第二層,先判斷n-1是否等於1,是就直接返回1,此時f(n-1)=1,且第二層結束,回到第一層,計算n*f(n-1),然後第一層結束;n-1不等於1,就返回 (n-1)*f(n-2),此時第三次呼叫這個函式,進入第三層,注意前面兩層還沒有結束,第二層在等第三層的f(n-2),第一層在等第二層的f(n-1)…以此類推,直到最後n==1,最後一層返回1,倒數第二層返回 2*f(1),即f(2),倒數第三層返回3*f(2),即f(3),…直到最後返回f(n)。
具體以f(4)來示範:
在這裡插入圖片描述


紅色箭頭表示每次呼叫函式,綠色箭頭表示當前層結束返回上一層。就好比你先往彈匣裡依次塞入4顆子彈,當最後一顆塞入時,扣動扳機,最後一顆子彈被打出,然後倒數第二顆塞入的上膛。
其實這也能表現出棧的特點之一就是‘後進先出’,或者‘先進後出’,只有當這一層上面的結束了,這一層才能結束。

能夠理解了,我們稍微總結一下遞迴的特點:

  • 遞迴必須要有一個明確的結束條件(防止死迴圈,報錯)
  • 每執行一次遞迴,都應該更接近你的結束條件
  • 遞迴效率不高,遞迴層次過多會導致棧溢位

我們再做一個小練習:
假設有一個從小到大排列的數字列表,裡面有無數個數據,使用遞迴找到我想要的數字,
data=[1,3,5,9,15,26,47,58,69,89,99,246,368,9854,…]


這裡我們要用二分查詢

def func(data,num):
    if len(data) > 1:
        mid = int(len(data)/2)
        if num > data[mid]: #  說明num在列表的右半部分
            print('%s 在列表的右半部分' % num)
            func(data[mid+1:],num)  # mid+1表示不將data[mid]算入
        elif num < data[mid]: #  說明num在列表的左半部分
            print('%s 在列表的左半部分' % num)
            func(data[:mid], num) # List顧頭不顧尾
        else: #  data[mid] == num
            print('已找到%s' % num)
    else: #  list中就一個數
        if data[0] == num:
            print('已找到%s' % num)
        else:
            print('列表中沒有該資料')

尾遞迴優化

其實Python中沒有實現這個優化-。-,捎帶提一下,尾遞迴優化是為了提高遞迴的效率,每一次呼叫遞迴都在return 中呼叫。我們知道函式中遇到return都代表函式的結束,那麼在return中呼叫函式本身,只要最後一次呼叫結束,那麼前面的幾次也就都會結束,不會在執行其他的東西。

def func();
	return func()  # 這就是尾遞迴

def func2():
	return n*func2() # 這裡雖然在return中呼叫了函式本身,但最後一層返回後,還是會繼續執行完n*這個事情,並沒有立即結束,所以不是尾遞迴

函式的內建模組

在這裡插入圖片描述

這裡面有詳細的介紹:
Python內建方法介紹