十二.函數式編程

函數是Python內建支持的一種封裝,咱們經過把大段代碼拆成函數,經過一層一層的函數調用,就能夠把複雜任務分解成簡單的任務,這種分解能夠稱之爲面向過程的程序設計。函數就是面向過程的程序設計的基本單元。html

而函數式編程(請注意多了一個「式」字)——Functional Programming,雖然也能夠歸結到面向過程的程序設計,但其思想更接近數學計算。python

咱們首先要搞明白計算機(Computer)和計算(Compute)的概念。編程

在計算機的層次上,CPU執行的是加減乘除的指令代碼,以及各類條件判斷和跳轉指令,因此,彙編語言是最貼近計算機的語言。api

而計算則指數學意義上的計算,越是抽象的計算,離計算機硬件越遠。app

對應到編程語言,就是越低級的語言,越貼近計算機,抽象程度低,執行效率高,好比C語言;越高級的語言,越貼近計算,抽象程度高,執行效率低,好比Lisp語言。編程語言

函數式編程就是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變量,所以,任意一個函數,只要輸入是肯定的,輸出就是肯定的,這種純函數咱們稱之爲沒有反作用。而容許使用變量的程序設計語言,因爲函數內部的變量狀態不肯定,一樣的輸入,可能獲得不一樣的輸出,所以,這種函數是有反作用的。函數式編程

函數式編程的一個特色就是,容許把函數自己做爲參數傳入另外一個函數,還容許返回一個函數!函數

Python對函數式編程提供部分支持。因爲Python容許使用變量,所以,Python不是純函數式編程語言。測試

高階函數

高階函數英文叫Higher-order function。什麼是高階函數?咱們以實際代碼爲例子,一步一步深刻概念。ui

變量能夠指向函數

以Python內置的求絕對值的函數abs()爲例,調用該函數用如下代碼:

#coding=utf-8  
print abs(-10)

可是,若是隻寫abs呢?

>>> abs
<built-in function abs>

可見,abs(-10)是函數調用,而abs是函數自己。

要得到函數調用結果,咱們能夠把結果賦值給變量:

>>> x = abs(-10)
>>> x
10

可是,若是把函數自己賦值給變量呢?

>>> f = abs
>>> f
<built-in function abs>

結論:函數自己也能夠賦值給變量,即:變量能夠指向函數。

若是一個變量指向了一個函數,那麼,能否經過該變量來調用這個函數?用代碼驗證一下:

函數名也是變量

那麼函數名是什麼呢?函數名其實就是指向函數的變量!對於abs()這個函數,徹底能夠把函數名abs當作變量,它指向一個能夠計算絕對值的函數!

若是把abs指向其餘對象,會有什麼狀況發生

>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

abs指向10後,就沒法經過abs(-10)調用該函數了!由於abs這個變量已經不指向求絕對值函數而是指向一個整數10

固然實際代碼絕對不能這麼寫,這裏是爲了說明函數名也是變量。要恢復abs函數,請重啓Python交互環境。

注:因爲abs函數其實是定義在import builtins模塊中的,因此要讓修改abs變量的指向在其它模塊也生效,要用import builtins; builtins.abs = 10

傳入函數

既然變量能夠指向函數,函數的參數能接收變量,那麼一個函數就能夠接收另外一個函數做爲參數,這種函數就稱之爲高階函數。

一個最簡單的高階函數:

當咱們調用add(-5, 6, abs)時,參數xyf分別接收-56abs,根據函數定義,咱們能夠推導計算過程爲:

#coding=utf-8  
def add(x, y, f):
    return f(x) + f(y)

print add(-5,6,abs)

編寫高階函數,就是讓函數的參數可以接收別的函數。

map/reduce

Python內建了map()reduce()函數。

若是你讀過Google的那篇大名鼎鼎的論文「MapReduce: Simplified Data Processing on Large Clusters」,你就能大概明白map/reduce的概念。

咱們先看map。map()函數接收兩個參數,一個是函數,一個是Iterablemap將傳入的函數依次做用到序列的每一個元素,並把結果做爲新的Iterator返回。

舉例說明,好比咱們有一個函數f(x)=x2,要把這個函數做用在一個list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就能夠用map()實現以下:

map\

如今,咱們用Python代碼實現:

#coding=utf-8  
def f(x):
    return x * x
r = map(f,[1, 2, 3, 4, 5, 6, 7, 8, 9])
print list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

map()傳入的第一個參數是f,即函數對象自己。因爲結果r是一個IteratorIterator是惰性序列,所以經過list()函數讓它把整個序列都計算出來並返回一個list。

你可能會想,不須要map()函數,寫一個循環,也能夠計算出結果:

#coding=utf-8  
L = []
def f(x):
     return x * x
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
    L.append(f(n))
print(L)

的確能夠,可是,從上面的循環代碼,能一眼看明白「把f(x)做用在list的每個元素並把結果生成一個新的list」嗎?

因此,map()做爲高階函數,事實上它把運算規則抽象了,所以,咱們不但能夠計算簡單的f(x)=x2,還能夠計算任意複雜的函數,好比,把這個list全部數字轉爲字符串:

的確能夠,可是,從上面的循環代碼,能一眼看明白「把f(x)做用在list的每個元素並把結果生成一個新的list」嗎?

因此,map()做爲高階函數,事實上它把運算規則抽象了,所以,咱們不但能夠計算簡單的f(x)=x2,還能夠計算任意複雜的函數,好比,把這個list全部數字轉爲字符串:

#coding=utf-8  
print list(map(str,[1,2,3,4,5,6,7,8,9]))

只須要一行代碼。

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

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

比方說對一個序列求和,就能夠用reduce實現:

#coding=utf-8  
from functools import reduce
def f(x):
    return x * x
def add(x,y):
    return x + y
print reduce(add,[1,3,5,7,9])
25

固然求和運算能夠直接用Python內建函數sum(),不必動用reduce

可是若是要把序列[1, 3, 5, 7, 9]變換成整數13579reduce就能夠派上用場:

from functools import reduce
def f(x):
    return x * x
def add(x,y):
    return x + y
def fn(x,y):
    return x * 10 + y
print reduce(fn,[1,3,5,7,9])
13579

這個例子自己沒多大用處,可是,若是考慮到字符串str也是一個序列,對上面的例子稍加改動,配合map(),咱們就能夠寫出把str轉換爲int的函數:

>>> from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> def char2num(s):
...     return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579

整理成一個str2int的函數就是

from functools import reduce

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    return reduce(fn, map(char2num, s))

還能夠用lambda函數進一步簡化成:

from functools import reduce

def char2num(s):
    return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

還能夠用lambda函數進一步簡化成:後面再細研究,在此不作演示,實際上是不知道呀

練習

1.利用map()函數,把用戶輸入的不規範的英文名字,變爲首字母大寫,其餘小寫的規範名字。輸入:['adam', 'LISA', 'barT'],輸出:['Adam', 'Lisa', 'Bart']

#coding=utf-8  
from functools import reduce
def normalize(name):
    return name.capitalize()
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize,L1))
print 'L2', L2

小總結:經測試 capitalize()是字符串方法,可將字符串首字母大寫,其他字母小寫化!,map2個參數,函數,隊列,調用的函數的參數將是隊列中的每個值,單獨分開來去掉用函數

2.Python提供的sum()函數能夠接受一個list並求和,請編寫一個prod()函數,能夠接受一個list並利用reduce()求積:

#coding=utf-8  
from functools import reduce
def prod(x,y):
    return x * y
L = [3, 5, 7, 9]
print reduce(prod,L)

總結,reduce有2個參數,(調用的函數,序列),被調用的函數應有2個參數--例子是整個的序列,不懂耶,之後再說--,2個參數分別對應着序列的順序排列變量

相關文章
相關標籤/搜索