上學期有上一門叫'人工智慧' 的課,老師強行要我們學了一個叫做prolog 的語言,哇那感覺確實難受,思維方式完全和之前學過的不一樣,寫個漢諾塔想了半天,最後還是在網上找了段代碼修改一下(怕被老師發現抄襲)才寫出來,貼一段出來感受一下:
hanoi(N) :- dohanoi(N, 'a', 'b', 'c'). dohanoi(0, _ , _ , _ ) :- !. dohanoi(N, A, B, C) :- N1 is N-1, dohanoi(N1, A, C, B), writeln([move, N, A-->C]), dohanoi(N1, B, A, C).
當時是差不多弄懂了,主要是資料實在太少,debug 都無從談起,一遇上bug 就gg,我現在自己看也有點頭暈。不過據說 prolog 當年能和 Lisp 一爭高下,最近對 Lisp 也有點興趣,等弄完這些就去參拜一下這類函數式語言。
何謂函數式程式設計?廖大這裡寫道:
函數式程式設計就是一種抽象程度很高的程式設計範式,純粹的函數式程式語言所寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函數內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。
可能看完還是有些不太理解,不急,先看完這幾個小節吧。
在數學和電腦科學中,高階函數是至少滿足下列一個條件的函數:
接受一個或多個函數作為輸入
輸出一個函數
是說,把函數本身當成參數傳遞,或是回傳一個函數。>>> min(1, 2) 1 >>> f = min >>> f(1, 2) 1 >>> f <built-in function min> >>> min <built-in function min>也可以給函數賦值(代碼接上):
>>> min = 10 >>> min(1, 2) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not callable >>> f(1, 2) 1 >>> min = f >>> min(1, 2) 1還可以傳參,例如,一個計算所有數字的和的函數:
>>> def add(a, b): ... return a+b ... >>> def mysum(f, *l): ... a = 0 ... for i in l: ... a = f(a, i) ... return a ... >>> mysum(add, 1, 2, 3) 6 >>> mysum(add, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 55當然,將這個f 換成乘法就是計算所有數字的乘積了。 再來看看 python 內建的一些高階函數,常常會用到。 map/reduce記得上學期上雲端運算的課程時依稀有聽過這個詞,不過這課很水,就沒怎麼聽,在這裡看到好像發現不太一樣? ? 不過沒啥說的,簡單說一下每個函數的作用。 對於 map,其計算式可以看成這樣:
map(f, [x1, x2, ..., xn]) = [f(x1), f(x2), ..., f(xn)]對於 reduce,其計算式可以看成這樣:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)廖大那裡說得很清楚啦。 filterfilter 和 map 函數類似,接受一個函數和 iterable,傳回也是一個 list,不過其功能是根據函數傳回值是否為 True 來判斷是否保留該值。例如:
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15]
>>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36]
import logging def log(func): def wrapper(*args, **kw): logging.warn("%s is running" % func.__name__) func(*args, **kw) return wrapper
def f(): print("in function f") f = log(f)這樣定義了之後,我們再呼叫f 函數:
>>> f() WARNING:root:f is running in function f使用@log 的結果與其一樣,其實@符號作為裝飾器的語法糖,與前面的賦值語句具有相同的功能,使代碼看起來更簡潔明了,避免再一次賦值操作,就像下面這樣:
@log def f(): print("in function f")含參數的裝飾器有時候我們還需要向裝飾器中傳入參數,例如,狀態,層次等信息,只需要在wrapper 函數外再'包裹'一層函數,如下所示:
import logging def log(level): def decorator(func): def wrapper(*args, **kw): logging.warn("%s is running at level %d" % (func.__name__, level)) return func(*args, **kw) return wrapper return decorator @log(2) def f(): print("in function f") >>> f() WARNING:root:f is running at level 2 in function f
#对于不加装饰器的 f,其 name 不变 >>> def f(): ... print("in function f") ... >>> f.__name__ 'f' #对于添加装饰器的函数,其 name 改变了 >>> @log ... def f(): ... print("in function f") ... >>> f.__name__ 'wrapper'
f = log(f)
使得f 指向修改為log(f) 的回傳值,即wrapper 函數。每次執行原函數 f 時,則會呼叫 wrapper 函數,在我們這個範例中,則是先列印日誌,然後執行原函數 f。不過這樣有一個問題,這樣使得原函數f 的元資訊被替換了,關於f 的許多資訊消失不見,這是很難令人接受的,不過好在我們有functools 模組,修改函數為:
import functools import logging def log(func): functools.wraps(func) def wrapper(*args, **kw): logging.warn("%s is running" % func.__name__) func(*args, **kw) return wrapper >>> @log ... def f(): ... print("in function f") ... >>> f.__name__ 'f'另外,還可以對同一個函數添加多個裝飾器:
@a @b @c def f (): # 等价于 f = a(b(c(f)))總結關於函數式編程我也不是很了解,這裡只是大概了解了一下其概念吧,平時肯定還是使用命令式編程用得多。不過有語言是純函數式語言,例如 Haskell 或 Lisp,學習它們會讓人打開一種新思路。 更多[python] 初探'函數式程式設計'相關文章請關注PHP中文網!