摘要:前言繼續向下看廖大教程,看到了函數式編程這一節,當時是覺得沒啥用直接跳過了,這次準備要仔細看一遍了,并記錄下一些心得。
前言
繼續向下看廖大教程,看到了函數式編程這一節,當時是覺得沒啥用直接跳過了,這次準備要仔細看一遍了,并記錄下一些心得。
函數式編程上學期有上一門叫 "人工智能" 的課,老師強行要我們學了一個叫做 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>>> min
也可以給函數賦值(代碼接上):
>>> min = 10 >>> min(1, 2) Traceback (most recent call last): File "", line 1, in 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
sorted 函數同樣是一個高階函數,對參數 key 傳遞函數可以將需要排列的序列經過 key 函數處理后再進行排序,不過不會改變序列的值,例如:
>>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36]裝飾器(decorator)
匿名函數就不說了,以后用時再仔細看吧,裝飾器我記得之前看 flask 的時候都研究了好久,這次再來復習一下。
簡單裝飾器首先是一個簡單的裝飾器,在每次調用函數前打印出日志:
import logging def log(func): def wrapper(*args, **kw): logging.warn("%s is running" % func.__name__) func(*args, **kw) return wrapper
這就是一個極其簡單的裝飾器,如何使用它呢?我最先看到的用法是在需要裝飾的函數前添加@,但其實這是 Python 的一個語法糖,最原始的用法反而更能讓人理解,先定義一個函數 f:
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 屬性:
#對于不加裝飾器的 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,學習它們會使得人打開一種新思路。
以上~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/38437.html
摘要:前菜在我們使用的過程很多時候會用到運算例如輸出不光在加法中使用在字符串的拼接也同樣發揮這重要的作用例如輸出同樣的在列表中也能使用例如輸出為什么上面不同的對象執行同一個會有不同的效果呢這就涉及到的重載然而這不是本文要討論的重點上面的只是前菜而 前菜 在我們使用Python的過程, 很多時候會用到+運算, 例如: a = 1 + 2 print a # 輸出 3 不光在加法中使用, 在...
摘要:環境問題發現最近剛從換到下搬磚,發現在跑的好好的代碼,在下終端老是報錯還是編碼錯誤注冊失敗坦白說,看到這個錯誤好無奈。既然能在跑,換到就出錯,那多半是環境問題了,然后我就開始了我的調試追蹤之旅了跟蹤調試先前調試都是一晃而過,只看結果。 環境:mac 10.12 python3 django 1.10 問題發現 最近剛從arch 換到 mac下搬磚, 發現在arch跑的好好的代碼,...
摘要:原因在安裝時,默認的編碼是,當程序中出現非編碼時,的處理常常會報這樣的錯,不過在就不會有這樣的問題。 1、原因 python2.7在安裝時,默認的編碼是ascii,當程序中出現非ascii編碼時,python的處理常常會報這樣的錯,不過在python3就不會有這樣的問題。 2、解決辦法 臨時解決方法: 代碼中加入如下三行import sys reload(sys) sys.setd...
摘要:起初是群里一個哥們這句話報錯。我竟然沒看懂代碼如下其實這個代碼不能很好的反應問題。來看以下兩個我一開始沒理解這個問題??吹胶痛致缘睦斫獬刹荒軌蛟陂]包函數中改變上層函數的變量。實際上是,如果一個變量被賦值,那么會認為其為局部變量。 起初是群里一個哥們這句話報錯。后來之前的一個實習生,給出了鏈接來解釋這個問題。 我竟然沒看懂.... http://stackoverflow.com/que...
摘要:原因很簡單,因為中的代表的就是當前執行的模塊名。缺點就是主程序會受待執行程序的影響,會出現待執行程序中拋異常或主動退出會導致主程序也退出的尷尬問題??偨Y來說就是,一個是在子進程中執行代碼,一個是在當前進程中執行代碼。 showImg(https://segmentfault.com/img/remote/1460000018607395?w=502&h=318); 相信剛接觸Pytho...
閱讀 737·2021-11-11 16:54
閱讀 3053·2021-09-26 09:55
閱讀 2002·2021-09-07 10:20
閱讀 1198·2019-08-30 10:58
閱讀 1039·2019-08-28 18:04
閱讀 698·2019-08-26 13:57
閱讀 3583·2019-08-26 13:45
閱讀 1150·2019-08-26 11:42