1. 程式人生 > >python學習筆記(四)——高階函式

python學習筆記(四)——高階函式

一、函數語言程式設計

    函式是Python內建支援的一種封裝,我們通過把大段程式碼拆成函式,通過一層一層的函式呼叫,可以把複雜任務分解成簡單的任務,這種分解可以稱之為面向過程的程式設計。函式就是面向過程的程式設計的基本單元。而函數語言程式設計(-- Functional Programming),雖然也可以歸納到面向過程的程式設計,但其思想更接近”數學計算“。

    首先讓我們明瞭計算機(Computer)與計算(Compute)的概念。在計算機的層次上,CPU執行的是加減乘除的指令程式碼,以及各種條件判斷和跳轉指令,所以,組合語言是最貼近計算機的語言。而計算則是指數學意義上的計算,越是抽象的計算,離計算機硬體越遠。對應到程式語言,就是越低階的語言,越貼近計算機,抽象程度低,執行效率高,比如C語言;越高階的語言,越貼近計算,抽象程度高,執行效率低,比如Lisp語言。

    函數語言程式設計就是一種抽象程度很高的程式設計正規化,純粹的函數語言程式設計語言編寫的函式沒有變數,因此,任意一個函式,只要輸入是確定的,輸出就是確定的,這種純函式我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函式內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函式是有副作用的。函數語言程式設計的一個特點就是,允許把函式本身作為引數傳入另一個函式,還允許返回一個函式!

    Python對函數語言程式設計提供部分支援。由於Python允許使用變數,因此,Python不是純函數語言程式設計語言。

二、高階函式

    高階函式英文叫Higher-order function。什麼是高階函式?我們以實際程式碼為例子,一步一步深入概念

    首先,變數也可以指向函式!

        以Python內建的求絕對值的函式abs()為例,呼叫該函式用以下程式碼:

abs(-10)

        但是,如果只寫abs呢?

>>> abs

<built-in function abs>

        可見,abs(-10)是函式呼叫,而abs是函式本身。

        要獲得函式呼叫結果,我們可以把結果賦值給變數:

>>> x = abs(-10)

>>> x

10

        但是,如果把函式本身賦值給變數呢?

>>> f = abs

>>> f

<built-in function abs>

    結論:函式本身也可以賦值給變數,即:變數可以指向函式。

    如果一個變數指向了一個函式,那麼,可否通過該變數來呼叫這個函式?用程式碼驗證一下:

>>> f = abs

>>> f(-10)

10

    成功!說明變數f現在已經指向了abs函式本身。直接呼叫abs()函式和呼叫變數f()完全相同。

    所以,函式名其實也就是變數!!!

    那麼函式名是什麼呢?函式名其實就是指向函式的變數!對於abs()這個函式,完全可以把函式名abs看成變數,它指向一個可以計算絕對值的函式!如果把abs指向其他物件,會有什麼情況發生?

    把abs指向10後,就無法通過abs(-10)呼叫該函數了!因為abs這個變數已經不指向求絕對值函式而是指向一個整數10!當然實際程式碼絕對不能這麼寫,這裡是為了說明函式名也是變數。要恢復abs函式,請重啟Python互動環境。

    注:由於abs函式實際上是定義在import builtins模組中的,所以要讓修改abs變數的指向在其它模組也生效,要用import builtins; builtins.abs = 10

    既然變數可以指向函式,函式的引數能接收變數,那麼一個函式就可以接收另一個函式作為引數,這種函式就稱之為高階函式。

def add(x, y, f):
    return f(x) + f(y)

res = add(-1, -19, abs)
print(res)

    這是一個最簡單的高階函式

    所以編寫高階函式,就是讓函式的引數能夠接收別的函式。

    小結:

        把函式作為引數傳入,這樣的函式稱為高階函式,函數語言程式設計就是指這種高度抽象的程式設計正規化。

二、map與reduce

    Python內建了map()和reduce()函式。

    (一)

        我們先看map。map()函式接收兩個引數,一個是函式,一個是Iterable,map將傳入的函式依次作用到序列的每個元素,並把結果作為新的Iterator返回。舉例說明,比如我們有一個函式f(x)=x2,要把這個函式作用在一個list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()。

def f(x):
    return x * x
r = map(f,[1,3,5,7,9])
print(list(r))

        map()傳入的第一個引數是f,即函式物件本身。由於結果r是一個Iterator,Iterator是惰性序列,因此通過list()函式讓它把整個序列都計算出來並返回一個list。

        而map()作為高階函式,事實上它把運算規則抽象了,因此,我們不但可以計算簡單的f(x)=x2,還可以計算任意複雜的函式,比如,把這個list所有數字轉為字串:

    (二)

        再看reduce的用法。reduce把一個函式作用在一個序列[x1, x2, x3, ...]上,這個函式必須接收兩個引數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

        讓我們來看幾個例子:

from functools import reduce

#累加得到1~9的奇數之和
def add(x,y):
    return x + y
num = reduce(add,[1,3,5,7,9])
print(num)

#計算5!
def prod(x,y):
    return x * y

res = reduce(prod, [1,2,3,4,5])
print(res)

#將列表[0~9]轉換為int:1234567890
def fn(x,y):
    return 10*x + y
real = reduce(fn, [1,2,3,4,5,6,7,8,9,0])
print(real)
#這個例子本身沒多大用處,但是,如果考慮到字串str也是一個序列,對上面的例子稍加改動,配合map(),我們就可以寫出把str轉換為int的函式

#將字串轉換為int放入列表中
def char2num(x):
    #通過字典中鍵值對的方式進行資料型別更改
    digits = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}
    return digits[x]

it = reduce(fn, map(char2num, ['1','2','3','4','5','6','7','8','9','0']))
print(it)

三、實驗:

    str 轉 float,即將一個例如‘1234.56789’的字串轉換為float。

    自己寫函式,不使用python自帶函式或方法。

#str 轉 float
from functools import reduce
def str2float(x,y):
    #根據分析,點號前面的數值是x*10+y進行累加的,點號後則是x+y*10^n(n是y的位數)累加的,而當搜尋到點號時,則返回x本身的規律,所以有了以下判斷方式
    if L.index(y) < L.index('.'):
        return x*10 + y
    if L.index(y) == L.index('.'):
        return x
    if L.index(y) > L.index('.'):
        return x + y*(0.1**(L.index(y)-L.index('.')))

def char2num(x):
    #因為是浮點數,所以多了點號在字典中
    digits = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'.':'.'}
    return digits[x]

L = list(map(char2num, list('3.456')))
print(L)
result = reduce(str2float, L)
print(result)
#問題,為什麼當輸入3.456時,輸出是3.4559999999999995???

相關推薦

no