天天看點

python學習第十一天 -- 函數式程式設計

在介紹函數式程式設計之前,先介紹幾個概念性的東西。

什麼是函數式程式設計?

函數式程式設計的特點:

1.把計算視為函數而非指令;

2.純函數式程式設計:不需要變量,沒有副作用,測試簡單;

3.支援高階函數,代碼簡潔。

什麼是高階函數?

能接收函數做參數的函數,稱為高階函數。

高階函數的特點:

1.變量可以指向函數

2.函數的參數可以接收變量

3.一個函數可以接收另一個函數作為參數

Python支援的函數式程式設計?

1.不是純函數式程式設計:允許有變量

2.支援高階函數:函數也可以作為變量傳入

3.支援閉包:有了閉包就能傳回函數

4.有限度的支援匿名函數

關于匿名函數和閉包的介紹,後面會一一介紹,暫時不在這裡詳細介紹。

高階函數-把函數作為參數

>>> def fn(x,y,f):
...     return f(x)+f(y)
...
>>> import math
>>> fn(4,16,math.sqrt)
6.0      

python内置的幾個常用的高階函數

map():接收一個函數f和一個list對象,函數f作用于list對象的每一個元素,并傳回一個新的list對象。

>>> def fn(x):
...     return x*x;
...
>>> print map(fn,range(1,11))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]      

reduce():也是接收函數f和一個list對象,但是接收函數f必須傳入兩個參數,然後對lsit中的每一個元素反複調用函數f,并傳回最終結果值。

>>> def prod(x,y):
...     return x*y
...
>>> print reduce(prod,range(1,6))
120 //1 *2*3*4*5      

filter():接收函數f和一個list對象,函數f的作用是對每一個list對象的元素進行判斷,傳回true 和false。filter()根據判斷結果自動過濾掉不符合的元素,傳回符合條件的元素組成的新list。

>>> def is_even_number(x):
...     return x%2==0
...
>>> filter(is_even_number,range(1,11))
[2, 4, 6, 8, 10]      

上面針對高階函數講解,大家應該了解了函數是可以作為參數的,那麼既然函數可以做參數,那麼也可以傳回函數。

傳回函數

>>> def f():
...     print 'call f()....'
...     //定義函數g:
...     def g():
...          print 'call g()....'
        //傳回函數g:
...     return g
...
>>> x = f() //調用f()
call f()....
>>> x 
<function g at 0x7f62fab01c80>
>>> x() //調用x()就是執行g()函數定義的代碼
call g()....      

舉個例子:編寫一個函數,接收一個list,傳回一個函數,傳回函數可以計算參數的乘積。

>>> def fn(x,y):
...     return x*y;
...
>>> def calc_prod(lst):
...     def my_calc():
...         return reduce(fn,lst)
...     return my_calc
...
>>> f = calc_prod(range(1,5))
>>> print f()
24      

閉包

内層函數引用外層函數的變量,然後傳回内層函數的情況,稱為閉包。

閉包的特點:傳回的函數引用了外層函數的局部變量。是以,要正确使用閉包,就能確定引用的局部變量在函數傳回後不能改變。舉例說明:

//希望一次傳回3個函數,分别計算1x1,2x2,3x3
>>> def count():
...     lst = []
...     for i in range(1,4):
...         def f():
...              return i*i
...         lst.append(f)
...     return lst
...
>>> f1,f2,f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9      

得到的結果并不是我們預期的1,4,9.

lst.append(f),隻是将每一個f()的引用儲存進了list,并沒有進行對于i的計算,是以導緻最後在運作了f(1)之後,i已經變為了3.

是以,傳回函數不要引用任何循環變量,或者後續會發生變化的變量。

那麼怎麼改寫上述的代碼,得到想要的1,4,9.值。

方法一:

>>> def count():
...     lst = []
...     for i in range(1,4):
...          def f():
...               return i*i
...          lst.append(f()) //很大的不同,f 變為了f(),這一步的時候已經進行了i*i的運算
...     return lst
...
>>> f1,f2,f3 = count()
>>> print f1,f2,f3
1 4 9      

方法二:

>>> def count():
...     lst = []
...     for i in range(1,4):
...          def f(m=i)://傳入參數
...               return m*m
...          lst.append(f) 
...     return lst
...
>>> f1,f2,f3 = count()
>>> print f1(),f2(),f3()
1 4 9      

問題的産生是因為函數隻在執行時才去擷取外層參數i,若函數定義時可以擷取到i,問題便可解決。而預設參數正好可以完成定義時擷取i值且運作函數時無需參數輸入的功能,是以在函數f()定義中改為f(m = i),函數f傳回值改為m*m即可.

匿名函數

關鍵字 lambda表示匿名函數,冒号前面的x表示函數參數。

匿名函數有個限制,就是隻能有一個表達式,不能寫return,傳回值就直接寫表達式的結果。

例子一:

>>> map(lambda x:x*x,range(1,5))
[1, 4, 9, 16]      

lambda x:x*x,可以看做是

>>> def f(x):
...     return x*x      

例子二:

>>> myabs = lambda x:-x if x<0 else x
>>> myabs(-1)
1
>>> myabs(2)
2      

lambda x:-x if x<0 else x可以看做是

>>> def f(x):
...     x = -x
...     if x<0:
...         return -x
...     else:
...         return x      

小結:這一節主要講解python函數式程式設計概念及其python高階函數的定義及其使用。

繼續閱讀