Python函數式編程學習:lambda, map, reduce, filter

以前也學過函數式編程,基本也就是函數嵌套,沒有深入學習。最近重寫代碼,重新學習了函數式編程,所以寫了這篇blog。

首先介紹下函數式編程的定義:來自維基百科點擊打開鏈接

函數式編程Functional programming)或者函數程序設計,又稱泛函編程,是一種編程範型,它將計算機運算視爲數學上的函數計算,並且避免使用程序狀態以及易變對象。函數編程語言最重要的基礎是λ演算(lambda calculus)。而且λ演算的函數可以接受函數當作輸入(引數)和輸出(傳出值)。

函數式編程的幾個特性:

1、first class functions:

指的是函數與其他數據類型一樣,處於平等地位,可以賦值給其他變量,也可以作爲參數,傳入另一個函數,或者作爲別的函數的返回值。

【2014.05.22更新:函數作爲返回值

>>> def later_sum(*args):
	def sum():
	    k = 0
	    for i in args:
		k += i
	    return k
	return sum

>>> f = later_sum(1, 2, 3, 4, 5)
>>> f
<function sum at 0x02A24370>
>>> f()
15
>>> f1 = later_sum(1, 2, 3, 4, 5)
>>> f1()
15
>>> f2 = later_sum(1, 2, 3, 4, 5)
>>> f2()
15
>>> f1 ==f2
False
>>> 
可以看得出,當我們調用later_sum()時,返回的是1個函數並給它起名f(本質上爲return會來的sum函數)。當我們調用f這個函數時候(f()函數名字+()爲函數調用),也就是調用了內層函數sum()。所以f()的值爲15,這就是延遲使用!像這樣,函數內部又定義函數,內部函數可以引用外部函數的參數,調用外部函數時,外部函數的參數保存在返回的函數裏,這種形式稱爲閉包(Closure)。

>>> def later_sum(*args):
	k = 0
	def sum():
	    for i in args:
		k += i
	    return k
	return sum

>>> f = later_sum(1, 2, 3, 4, 5)
>>> f()

Traceback (most recent call last):
  File "<pyshell#115>", line 1, in <module>
    f()
  File "<pyshell#113>", line 5, in sum
    k += i
UnboundLocalError: local variable 'k' referenced before assignment
>>> 

有的Blog說:內部函數可以引用外部函數的參數和局部變量是不對的!可以由上面代碼看出。

【更新於2014.07.31:python3中可以通過nonlocal關鍵字徹底搞定上面的例子!

>>> def later_sum(*args):  
	k = 0  
	def sum():
	    nonlocal k
	    for i in args:  
	        k += i  
	    return k  
	return sum

>>> later_sum(1, 2, 3, 4, 5)()
15
>>> 

還需要注意的是f1()和f2()不是一回事!


2、mmutable data:

函數式編程只返回新值,不修改變量。例如:

def add_1(ago):
    return ago + 1

    我們定義一個變量a的值爲1,我們調用add_1(a)時,a的值不會改變,函數返回值爲2。也就是說add_1()不會改變外部變量的值且函數內部沒有依賴外部變量的值,和外部無關,最後返回一個值給你,這就是函數式編程。

    可能看到這,很多人覺得這也太簡單了,我平時代碼就這麼寫的。很顯然,被N多人推崇N久的東西不會這麼簡單。我們繼續學習。

3.Referential transparency:

引用透明,也就是說函數結果只依賴於參數的改變,參數不變,輸出不會變。

以上也是總結書本和網友所說的。

再來一個簡單的例子:

>>> def add(x):
      def x_add_y(y):
         return x + y
      return x_add_y
>>> add(3)(4)
7

    這段程序什麼意思呢,首先add(3)返回了另一個函數x_add_y()然後再調用x_add_y(4)。本質上就是Currying技術:把一個函數的多個參數分解成多個函數,然後把函數層層封裝。這時候可能覺得這樣做還是沒用,可能還會造成閱讀代碼變困難。是的,我們應該學習下高級點的東西!這就是lambda, map, reduce, filter!

Lambda:

def name(arguments):
    return expression

lambda本質上是一個表達式,他定義了一個匿名函數,返回的是一個函數對象。官方文檔原文:Lambda expressions (sometimes called lambda forms) have the same syntactic position as expressions. They are a shorthand to create anonymous functions; the expression lambdaarguments: expression yields a function object. The unnamed object behaves like a function object defined with

>>> func = lambda x, y: x + y
>>> print type(func)
<type 'function'>
>>> func(1, 2)
3

    本質上,這個上面那個add()沒有區別,都是做x+y的運算。lambda簡化了函數的書寫,一行代碼做了前面4行代碼的事,代碼看起來更簡潔了。也不用擔心代碼不能複用,上面的func(1, 2)就是很好的證明,你當然可以用func(10010, 10086)了。他的作用不限如此,在map, reduce, filter中的應用我們就可以看得到!

以下例子內容來源於官方文檔:點擊打開鏈接 更進一步學習推薦一篇論文:MapReduce: Simplified Data Processing on Large Clusters 

Map

map(function, sequence) calls function(item) for each of the sequence's items and returns a list of the return values. For example, to compute some cubes:

        >>> map(lambda x: x*x*x, range(1, 11))
        [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
        >>>

More than one sequence may be passed; the function must then have as many arguments as there are sequences and is called with the corresponding item from each sequence (or None if some sequence is shorter than another). If None is passed for the function, a function returning its argument(s) is substituted.

Combining these two special cases, we see that map(None, list1, list2) is a convenient way of turning a pair of lists into a list of pairs. For example:

        >>> seq = range(8)
        >>> map(None, seq, map(lambda x: x*x, seq))
        [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)]
        >>>

也就是說map(function, sequence).是把sequence中的值當參數逐個傳給function,返回一個包含函數執行結果的list。如果function有兩個參數,即map(function, sequence1, sequence2)。

>>> map(lambda x, y: x + y, range(9), range(9))
[0, 2, 4, 6, 8, 10, 12, 14, 16]
>>> map(lambda x: x*2, range(9))
[0, 2, 4, 6, 8, 10, 12, 14, 16]
>>> 

Reduce

reduce(function, sequence) returns a single value constructed by calling the (binary) function on the first two items of the sequence, then on the result and the next item, and so on. For example, to compute the sum of the numbers 1 through 10:

        >>> reduce(lambda x, y: x+y, range(1, 11))
        55
        >>>

If there's only one item in the sequence, its value is returned; if the sequence is empty, an exception is raised.

A third argument can be passed to indicate the starting value. In this case the starting value is returned for an empty sequence, and the function is first applied to the starting value and the first sequence item, then to the result and the next item, and so on. For example,

        >>> def sum(seq):
        ...     return reduce(lambda x, y: x+y, seq, 0)
        ... 
        >>> sum(range(1, 11))
        55
        >>> sum([])
        0
        >>>
reduce需要2個參數,一個是函數,一個是sequence。數列爲空時,會報錯,數列正確時,會返回計算後的值,可以有第三個參數,作爲starting value,也就是說函數值最終都要加上這個start value。以下例子爲證:
>>> reduce(lambda x, y: x+y, range(5), 100)
110
>>> reduce(lambda x, y: x+y, range(5))
10
>>> reduce(lambda x, y: x+y, range(5), 10086)
10096
>>> 
reduce工作機制是,前面的fuction必須是需要兩個參數的函數!因爲reduce本身就是把sequence的前兩個數傳遞進function並把function的返回值和第三個數再傳遞進fuction,類推!最後只返回一個結果!

Filter

filter(function, sequence) returns a sequence (of the same type, if possible) consisting of those items from the sequence for which function(item) is true. For example, to compute some primes:

        >>> filter(lambda x: x%2 != 0 and x%3 != 0, range(2, 25))
        [5, 7, 11, 13, 17, 19, 23]
        >>>

filiter返回值和sequence相關!如果function返回值是True,則添加進結果list中,當然了不一定是list,這要看sequence是什麼!以下是我寫的例子:

>>> filter(lambda x: len(x) != 0, 'python')
'python' # str
>>> filter(lambda x: len(x) != 0, ['python'])
['python'] # list
>>> filter(lambda x: len(x) != 0, ('python'))
'python' # str
>>> filter(lambda x: len(x) != 0, ('python',))
('python',) # tuple

【更新於:2014.07.05

Construct a list from those elements of iterable for which function returns true. iterable may be either a sequence, a container which supports iteration, or an iterator.If iterable is a string or a tuple, the result also has that type; otherwise it is always a list. If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.】

總結:

1、函數式編程在數學上應用很犀利!

2、代碼變簡單了,雖然剛開始看起來很彆扭。

3、再也不用考慮循環體中各種變量了。

4、我只用表達,我是要幹什麼,而不是我怎麼做的。

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