Python 函數式編程

1.什麼是函數式編程

Wiki上對Functional Programming的定義:html

In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions[1] or declarations[2] instead of statements. In functional code, the output value of a function depends only on the arguments that are input to the function, so calling a function f twice with the same value for an argument x will produce the same result f(x) each time.** Eliminating side effects, i.e. changes in state that do not depend on the function inputs, can make it much easier to understand and predict the behavior of a program, which is one of the key motivations for the development of functional programming. **python

"函數式編程"是一種"編程範式"(programming paradigm),一種構建計算機程序結構和元素的風格/模式。它把運算看成是數據函數的計算,避免了多變的狀態和易變的數據。它是一種聲明性編程範式,意味着程序是由表達式或聲明完成的而不是語句。在函數代碼中,函數的輸出值只依賴於對這個函數的輸入,所以以一樣的參數值x調用函數f兩次,每次獲得的結果是相同的f(x)。消除反作用,即狀態的改變再也不依賴於函數的輸入,這使理解和預測程序的行爲更加容易,這也是發展函數式編程的主要動力。****express

所謂"反作用"(side effect),指的是函數內部與外部互動(最典型的狀況,就是修改全局變量的值),產生運算之外的其餘結果。
函數式編程強調沒有"反作用",意味着函數要保持獨立,全部功能就是返回一個新的值,沒有其餘行爲,尤爲是不得修改外部變量的值。編程

和聲明性編程對應的是命令式編程(imperative programming), 它在源程序中使用命令改變狀態,最簡單的例子就是賦值。存在的反作用是可能改變程序狀態的值。所以函數沒有返回值是有意義的。因爲它們缺乏引用透明性(referential transparency),即根據程序的執行狀態在不一樣的時間,相同的表達式可能會產生不一樣的結果。閉包

引用透明(Referential transparency),指的是函數的運行不依賴於外部變量或"狀態",只依賴於輸入的參數,任什麼時候候只要參數相同,引用函數所獲得的返回值老是相同的。app

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

函數式編程就是如何編寫程序的方法論。
它屬於"結構化編程"的一種,主要思想是把運算過程儘可能寫成一系列嵌套的函數調用。舉例來講,如今有這樣一個數學表達式:ide

(1 + 2) * 3 - 4

傳統的過程式編程,可能這樣寫:函數式編程

var a = 1 + 2;
var b = a * 3;
var c = b - 4;

函數式編程要求使用函數,咱們能夠把運算過程定義爲不一樣的函數,而後寫成下面這樣:函數

var result = subtract(multiply(add(1,2), 3), 4);

這就是函數式編程。

2.函數即變量(first class function)

Python中一切皆爲對象。函數也不例外,且函數與其餘數據類型同樣,處於平等地位。
函數能夠賦值給變量,也能夠做爲參數,傳入另外一個函數,或者做爲別的函數的返回值。函數名其實就是指向函數的變量,它能夠被賦其餘值。

這樣才能快捷實施函數式編程,C語言裏函數與變量不平等的,或許藉助函數指針卻是能夠實現,但頗爲麻煩。

函數賦值給其餘變量

>>> abs(-10)
10
>>> foo = abs
>>> foo(-10)
10

函數名被賦其餘變量

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

函數做爲參數傳遞給另外一函數

>>> def add(x, y, foo):
...     return foo(x) + foo(y)
... 
>>> add(-10, 3, abs)
13

函數做爲返回值返回

>>> def foo():
    return abs
... ... 
>>> fun = foo()
>>> fun(-10)
10
3.匿名函數lambda

lambda表達式用於建立匿名函數,是一種簡化函數定義的快速方法。不像def那樣能夠執行多重語句和註釋,lambda形式定義的函數沒有def形式的強大。優勢是快速簡單,一條語句就可定義一個函數。
lambda語法以下:

lambda arguments: expression

lambda定義一個求和函數add。

add = lambda x, y: x + y

效果和def形式定義的是同樣的。

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

lambda表達式一般是直接嵌入在須要函數的地方,且該函數能使用一個表達式完整描述,不大會直接將lambda表達式命令成函數。這雖然很酷,可是不方便拓展,若是一開始就須要給函數命名,應該使用def形式定義。

4.編程風格

打印斐波那契數列前10個。

  • 命令式風格,使用了循環,有變量賦值語句。(如何解釋反作用的緣由?)
def fibonacci(n, first=0, second=1):
    while n != 0:
        print(first, end="\n") # side-effect
        n, first, second = n - 1, second, first + second # assignment
fibonacci(10)
  • 函數式風格,使用了遞歸方式
fibonacci = (lambda n, first=0, second=1:
    "" if n == 0 else
    str(first) + "\n" + fibonacci(n - 1, second, first + second))
print(fibonacci(10), end="")

此處的lambda表達式略微複雜,換成def的形式:

def fibonacci(n, first=0, second=1):
    if n == 0:
        return ''
    else:
        return str(first) + "\n" + fibonacci(n - 1, second, first + second)
print(fibonacci(10), end="")

循環是在描述咱們該如何地去解決問題。
遞歸是在描述這個問題的定義。

簡單點理解,函數式編程就是把函數當普通的變量或對象同樣使用,函數用作參數或返回值。函數自身帶有邏輯計算,當變量使用能夠獲得很是高效的代碼。

4.高階函數

傳統的說,Python是一種非函數式語言(non-functional),但在編碼時可使用函數式風格。Python中的lambda,高階函數(map, reduce, filter),偏函數(partial function),閉包(closure),裝飾器(decorator)都有函數式思想。經過 Python 中的高階函數稍稍體會下函數式編程的趣味。
把函數做爲參數傳入的函數,這樣的函數稱爲高階函數。Python中知名高階函數有map, reduce, filter。

  • map

    | map(func, *iterables) --> map object
    |
    | Make an iterator that computes the function using arguments from
    | each of the iterables. Stops when the shortest iterable is exhausted.

map()傳入的第一個參數是函數,第二個參數是Iterable對象,便可迭代的對象,如list,dict,str都是Iterable對象。map將函數func依次做用到Iterable對象的元素上,返回一map對象。(map對象是?)
以下將list中元素平方後獲得新的list。

>>> ret= map(lambda x: x*x, list(range(5)))
>>> print(ret)
<map object at 0x029AE4B0>
>>> print(list(ret))
[0, 1, 4, 9, 16]

不是很嚴格的map()實現:

from collections import Iterable

def map(func, a_iterable):
    result = []
    if isinstance(a_iterable, Iterable) is False:
        raise TypeError('%s object is not iterable' % type(a_iterable))
    else:
        for item in a_iterable:
            result.append(func(item)) 
    return result
  • reduce
    Python3裏 reduce() 在 functools 模塊下。

    functools.reduce = reduce(...)
    reduce(function, sequence[, initial]) -> value
    Apply a function of two arguments cumulatively to the items of a sequence,
    from left to right, so as to reduce the sequence to a single value.
    For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
    ((((1+2)+3)+4)+5). If initial is present, it is placed before the items
    of the sequence in the calculation, and serves as a default when the
    sequence is empty.

reduce()最少接收2個參數,最多接收3個參數,第2個參數要求可迭代的。reduce()做用效果:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

>>> from functools import reduce
>>> reduce(lambda x, y: x+y, [1, 2, 3], 10)
16
>>> reduce(lambda x, y: x+y, [1, 2, 3])
6

map() 和 reduce() 結合使用實現 str 到 int 的過程,簡潔又酷炫。

from functools import reduce

def str2int(a_str):
    
    char2int = lambda x :{'0':0, '1':1, '2':2, '3':3, '4': 4, '5': 5,
         '6': 6, '7':7, '8': 8, '9': 9}[x]

    return reduce(lambda x, y: x*10+y, map(char2int, a_str))

print(str2int('1234'))
  • filter

    class filter(object)
    | filter(function or None, iterable) --> filter object
    |
    | Return an iterator yielding those items of iterable for which function(item)
    | is true. If function is None, return the items that are true.

filter()也接收一個函數和一個序列。和 map()不一樣的時,filter()把傳入的函數依次做用於每一個元素, 而後根據返回值是 True 仍是 False 決定保留仍是丟棄該元素。
如獲得序列中的整數:

>>> ret = filter(lambda x: x if x>0 else None, [1, -2, 3, -4, 0])
>>> print(ret)
<filter object at 0x029AEEF0>
>>> print(list(ret))
[1, 3]
  • sorted

    sorted(iterable, key=None, reverse=False)
    Return a new list containing all items from the iterable in ascending order.
    |
    A custom key function can be supplied to customise the sort order, and the
    reverse flag can be set to request the result in descending order.

sorted()函數也是一個高階函數,它還能夠接收一個 key 函數來實現自定義的排序。
按絕對值排序

>>> sorted([1, -2, -3, 4], key=abs)
[1, -2, -3, 4]

問題:
有這樣一個 list :['1y','3m','1m','7d','5d','1d','12h','6h','3h']
其中 y 表示年, m 表示月, d 表示天, h 表示小時,前面的數字表示對應的時間
問如何用 sort 在一行內按照年>月>天>小時或者反過來的順序進行排序?

>>> a_list = ['1y','3m','1m','7d','5d','1d','12h','6h','3h']
>>> sorted(a_list, key = lambda x: {'y':365*24, 'm':30*24, 'd':24, 'h':1}[x[-1]] * int(x[:-1]))
['3h', '6h', '12h', '1d', '5d', '7d', '1m', '3m', '1y']

參考:

1.wiki-Functional programming
2.Python函數式編程指南(一):概述
3.函數式編程掃盲篇
4.函數式編程初探
5.函數式編程

完---若有錯漏,請指教:)

相關文章
相關標籤/搜索