摘要:前言繼續(xù)向下看廖大教程,看到了函數(shù)式編程這一節(jié),當(dāng)時是覺得沒啥用直接跳過了,這次準(zhǔn)備要仔細(xì)看一遍了,并記錄下一些心得。
前言
繼續(xù)向下看廖大教程,看到了函數(shù)式編程這一節(jié),當(dāng)時是覺得沒啥用直接跳過了,這次準(zhǔn)備要仔細(xì)看一遍了,并記錄下一些心得。
函數(shù)式編程上學(xué)期有上一門叫 "人工智能" 的課,老師強行要我們學(xué)了一個叫做 prolog 的語言,哇那感覺確實難受,思維方式完全和之前學(xué)過的不一樣,寫個漢諾塔想了半天,最后還是在網(wǎng)上找了段代碼修改一下(怕被老師發(fā)現(xiàn)抄襲)才寫出來,貼一段出來感受一下:
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).
當(dāng)時是差不多弄懂了,主要是資料實在太少,debug 都無從談起,一遇上 bug 就 gg,我現(xiàn)在自己看也有點頭暈。不過據(jù)說 prolog 當(dāng)年能和 Lisp 一爭高下,最近對 Lisp 也有點興趣,等弄完這些就去參拜一下這類函數(shù)式語言。
何謂函數(shù)式編程?廖大這里寫道:
函數(shù)式編程就是一種抽象程度很高的編程范式,純粹的函數(shù)式編程語言編寫的函數(shù)沒有變量,因此,任意一個函數(shù),只要輸入是確定的,輸出就是確定的,這種純函數(shù)我們稱之為沒有副作用。而允許使用變量的程序設(shè)計語言,由于函數(shù)內(nèi)部的變量狀態(tài)不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數(shù)是有副作用的。
可能看完還是有些不太理解,不急,先看完這幾個小節(jié)吧。
高階函數(shù)在數(shù)學(xué)和計算機科學(xué)中,高階函數(shù)是至少滿足下列一個條件的函數(shù):
接受一個或多個函數(shù)作為輸入
輸出一個函數(shù)
也就是說,把函數(shù)本身當(dāng)成參數(shù)傳遞,或者返回一個函數(shù)。
例如,可以像普通賦值一樣將函數(shù)賦值給變量:
>>> min(1, 2) 1 >>> f = min >>> f(1, 2) 1 >>> f>>> min
也可以給函數(shù)賦值(代碼接上):
>>> 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
還可以傳參,例如,一個計算所有數(shù)字的和的函數(shù):
>>> 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
當(dāng)然,將這個 f 換成乘法就是計算所有數(shù)字的乘積了。
再來看看 python 內(nèi)置的一些高階函數(shù),經(jīng)常會用到。
map/reduce記得上學(xué)期上云計算的課程時依稀有聽到過這個詞,不過這課很水,就沒怎么聽,在這里看到好像發(fā)現(xiàn)不太一樣??
不過沒啥說的,簡單說一下每個函數(shù)的作用。
對于 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 函數(shù)類似,接受一個函數(shù)和 iterable,返回也是一個 list,不過其功能是根據(jù)函數(shù)返回值是否為 True 來判斷是否保留該值。例如:
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 結(jié)果: [1, 5, 9, 15]sorted
sorted 函數(shù)同樣是一個高階函數(shù),對參數(shù) key 傳遞函數(shù)可以將需要排列的序列經(jīng)過 key 函數(shù)處理后再進行排序,不過不會改變序列的值,例如:
>>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36]裝飾器(decorator)
匿名函數(shù)就不說了,以后用時再仔細(xì)看吧,裝飾器我記得之前看 flask 的時候都研究了好久,這次再來復(fù)習(xí)一下。
簡單裝飾器首先是一個簡單的裝飾器,在每次調(diào)用函數(shù)前打印出日志:
import logging def log(func): def wrapper(*args, **kw): logging.warn("%s is running" % func.__name__) func(*args, **kw) return wrapper
這就是一個極其簡單的裝飾器,如何使用它呢?我最先看到的用法是在需要裝飾的函數(shù)前添加@,但其實這是 Python 的一個語法糖,最原始的用法反而更能讓人理解,先定義一個函數(shù) f:
def f(): print("in function f") f = log(f)
這樣定義了之后,我們再調(diào)用 f 函數(shù):
>>> f() WARNING:root:f is running in function f
使用 @log 的結(jié)果與其一樣,其實@符號作為裝飾器的語法糖,與前面的賦值語句具有相同的功能,使代碼看起來更簡潔明了,避免再一次賦值操作,就像下面這樣:
@log def f(): print("in function f")含參數(shù)的裝飾器
有時候我們還需要向裝飾器中傳入?yún)?shù),例如,狀態(tài),層次等信息,只需要在 wrapper 函數(shù)外再"包裹"一層函數(shù),如下所示:
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進一步理解
為了再進一步理解裝飾器,我們可以打印出函數(shù) f 的 name 屬性:
#對于不加裝飾器的 f,其 name 不變 >>> def f(): ... print("in function f") ... >>> f.__name__ "f" #對于添加裝飾器的函數(shù),其 name 改變了 >>> @log ... def f(): ... print("in function f") ... >>> f.__name__ "wrapper"
聯(lián)系到最前面的裝飾器賦值語句,就可以大致明白發(fā)生了什么:f = log(f) 使得 f 指向修改為 log(f) 的返回值,即 wrapper 函數(shù)。每次運行原函數(shù) f 時,則會調(diào)用 wrapper 函數(shù),在我們這個例子中,則是先打印日志,然后運行原函數(shù) f。
不過這樣有一個問題,這樣使得原函數(shù) f 的元信息被替換了,關(guān)于 f 的許多信息消失不見,這是很難令人接受的,不過好在我們有 functools 模塊,修改函數(shù)為:
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"
另外,還可以對同一個函數(shù)添加多個裝飾器:
@a @b @c def f (): # 等價于 f = a(b(c(f)))總結(jié)
關(guān)于函數(shù)式編程我也不是很了解,這里只是大概了解了一下其概念吧,平時肯定還是使用命令式編程用得多。不過有語言是純函數(shù)式語言,例如 Haskell 或 Lisp,學(xué)習(xí)它們會使得人打開一種新思路。
以上~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/38437.html
摘要:前菜在我們使用的過程很多時候會用到運算例如輸出不光在加法中使用在字符串的拼接也同樣發(fā)揮這重要的作用例如輸出同樣的在列表中也能使用例如輸出為什么上面不同的對象執(zhí)行同一個會有不同的效果呢這就涉及到的重載然而這不是本文要討論的重點上面的只是前菜而 前菜 在我們使用Python的過程, 很多時候會用到+運算, 例如: a = 1 + 2 print a # 輸出 3 不光在加法中使用, 在...
摘要:環(huán)境問題發(fā)現(xiàn)最近剛從換到下搬磚,發(fā)現(xiàn)在跑的好好的代碼,在下終端老是報錯還是編碼錯誤注冊失敗坦白說,看到這個錯誤好無奈。既然能在跑,換到就出錯,那多半是環(huán)境問題了,然后我就開始了我的調(diào)試追蹤之旅了跟蹤調(diào)試先前調(diào)試都是一晃而過,只看結(jié)果。 環(huán)境:mac 10.12 python3 django 1.10 問題發(fā)現(xiàn) 最近剛從arch 換到 mac下搬磚, 發(fā)現(xiàn)在arch跑的好好的代碼,...
摘要:原因在安裝時,默認(rèn)的編碼是,當(dāng)程序中出現(xiàn)非編碼時,的處理常常會報這樣的錯,不過在就不會有這樣的問題。 1、原因 python2.7在安裝時,默認(rèn)的編碼是ascii,當(dāng)程序中出現(xiàn)非ascii編碼時,python的處理常常會報這樣的錯,不過在python3就不會有這樣的問題。 2、解決辦法 臨時解決方法: 代碼中加入如下三行import sys reload(sys) sys.setd...
摘要:起初是群里一個哥們這句話報錯。我竟然沒看懂代碼如下其實這個代碼不能很好的反應(yīng)問題。來看以下兩個我一開始沒理解這個問題。看到和粗略的理解成不能夠在閉包函數(shù)中改變上層函數(shù)的變量。實際上是,如果一個變量被賦值,那么會認(rèn)為其為局部變量。 起初是群里一個哥們這句話報錯。后來之前的一個實習(xí)生,給出了鏈接來解釋這個問題。 我竟然沒看懂.... http://stackoverflow.com/que...
摘要:原因很簡單,因為中的代表的就是當(dāng)前執(zhí)行的模塊名。缺點就是主程序會受待執(zhí)行程序的影響,會出現(xiàn)待執(zhí)行程序中拋異常或主動退出會導(dǎo)致主程序也退出的尷尬問題??偨Y(jié)來說就是,一個是在子進程中執(zhí)行代碼,一個是在當(dāng)前進程中執(zhí)行代碼。 showImg(https://segmentfault.com/img/remote/1460000018607395?w=502&h=318); 相信剛接觸Pytho...
閱讀 741·2021-11-11 16:54
閱讀 3064·2021-09-26 09:55
閱讀 2014·2021-09-07 10:20
閱讀 1206·2019-08-30 10:58
閱讀 1055·2019-08-28 18:04
閱讀 706·2019-08-26 13:57
閱讀 3594·2019-08-26 13:45
閱讀 1159·2019-08-26 11:42