高階函數-遞歸

1.高階函數

1.1高階函數定義

變量可以指向函數,函數的參數能接受變量,那麼一個函數就可以接收另一個函數作爲參數,這種函數就稱爲高階函數。
只要滿足以下任意一個條件,即是高階函數
1.接收一個或多個函數作爲輸入
2.return返回另外一個函數

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

def add(x, y, func):
    return func(x) + func(y)
res = add(3, -6, abs)
print(res)

E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
9

Process finished with exit code 0

2.遞歸

2.1遞歸定義

在函數內部,可以調用其它函數。如果一個函數在內部調用自身,這個函數就是遞歸函數。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

def calc(n):
    print(n)
    if int(n/2) == 0:
        return n
    return calc(int(n/2))
calc(4)

E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
4
2
1

Process finished with exit code 0

2.2遞歸的執行過程

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

def calc(n):
    print(n)
    if int(n/2) > 0:
        calc(int(n/2))
    print(n)
calc(4)

E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
4
2
1
1
2
4

Process finished with exit code 0

高階函數-遞歸

2.3遞歸特性

1.必須有一個明確的結束條件
2.每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
3.遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用時通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以遞歸調用的次數過多,會導致棧溢出)

    #!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

def recursion(n):
    print(n)
    recursion(n + 1)

recursion(1)
運行
...
996
997
998
Process finished with exit code 1
"到998程序就自動停止了,是因爲遞歸的最大深度是有限制的,防止棧溢出"

2.4二分法查找

    #!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

data = [1, 3, 5, 7, 8, 10, 30, 34, 35]

def binary_search(source_data, search_data):
    print(source_data)
    if len(source_data) > 1:
        mid_data_index = int(len(source_data)/2)
        if source_data[mid_data_index] == search_data:
            print("excellent,you have found it!")
        elif source_data[mid_data_index] > search_data:
            source_data = source_data[0:mid_data_index]
            binary_search(source_data, search_data)
        elif source_data[mid_data_index] < search_data:
            source_data = source_data[mid_data_index + 1:]
            binary_search(source_data, search_data)
    else:
        if len(source_data) == 0:
            print("source data is null now!")
        else:
            if source_data[0] == search_data:
                print("you have found it!")
            else:
                print("there is not this number!")
binary_search(data, 30)

E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
[1, 3, 5, 7, 8, 10, 30, 34, 35]
[10, 30, 34, 35]
[10, 30]
excellent,you have found it!

Process finished with exit code 0
"找4"
binary_search(data, 4)
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
[1, 3, 5, 7, 8, 10, 30, 34, 35]
[1, 3, 5, 7]
[1, 3]
[]
source data is null now!

Process finished with exit code 0

2.5求階乘

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

def multi(n):
    if n == 1:
        return 1
    else:
        return n*multi(n-1)

print(multi(4))

E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
24

Process finished with exit code 0

2.6斐波那契

    #!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
# 1 1 2 3 5 8 13
a = 0
b = 1
n = 0
def fibs(a, b):
    global n
    a, b = b, a + b
    print(a)
    if n == 5:
        return
    n += 1
    fibs(a, b)

fibs(a,b)

E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
1
1
2
3
5
8

Process finished with exit code 0

2.7尾遞歸

再講遞歸特性時,我們說遞歸效率不高,因爲每遞歸一次,就多了一層棧,遞歸次數過多就會棧溢出,這也是Python默認會限制遞歸次數的原因。但有一種方式是可以在遞歸過程中不產生多層棧,即尾遞歸。
如果一個函數中所有遞歸形式的調用都出現在函數的末尾,我們稱這個遞歸函數是尾遞歸的。當遞歸調用時整個函數體重最後執行的語句且它的返回值不屬於表達式的一部分,這個遞歸調用就是尾遞歸。尾遞歸函數的特點是在迴歸過程中不用做任何操作,這個特性很重要,因爲大多數現代的編譯機會利用這個特點自動生成優化的代碼。
當編譯器檢測到一個函數調用時尾遞歸,就會覆蓋當前活動記錄,而不是在棧中去創建新的。編譯器可以做到這點,因爲遞歸調用時當前活躍期內最後執行的一條語句,於是當這個調用返回時幀棧中並沒有其他事情可做,因此也就沒有保存棧幀的必要了。通過覆蓋當前的棧幀而不是重新添加一個,這樣就節省了棧幀的使用,運行效率變高。

"尾遞歸的例子"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita

def calc(n):
    print(n)
    if n > 0:
        return calc(n-1)
calc(8)

那麼階乘的例子是尾遞歸嗎?
不是尾遞歸,因爲每個活躍的返回值都依賴於用n乘以下一個活躍期的返回值,因此每次調用產生的棧幀不得不保存在棧上直到下一個子調用的返回值確定。

python中實際是沒有做尾遞歸的優化,但別的語言有優化的

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