07.31 他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

Python 函數式編程入門教程

函數式編程源自於數學理論,它似乎也更適用於數學計算相關的場景,因此本文以一個簡單的數據處理問題為例,逐步介紹 Python 函數式編程從入門到走火入魔的過程。

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

一、什麼是函數式編程?

函數:function

函數式:functional,是一種編程範式

二、函數式編程的特點:

1)把計算視為函數而非指令

2)純函數式編程:不需要變量,沒有副作用,測試簡單

3)支持高階函數,代碼簡潔

三、Python支持的函數式編程

1)Python不是純函數式編程:允許有變量

2)Python支持高階函數:函數也可以作為變量傳入

3)Python支持閉包:有了閉包就能返回函數

4)有限度的支持你匿名函數

問題:計算 N 位同學在某份試卷的 M 道選擇題上的得分(每道題目的分值不同)。

首先來生成一組用於計算的偽造數據:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

四、入門

首先來看常規的面向過程編程風格,我們需要遍歷每個學生,然後遍歷每個學生對每道題目的答案並與真實答案進行比較,然後將正確答案的分數累計:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

如果你覺得上面的代碼非常直觀且合乎邏輯,那說明你已經習慣按照計算機的思維模式進行思考了。通過創建嵌套兩個 for 循環來遍歷所有題目答案的判斷和評分,這完全是為計算機服務的思路,雖然說 Python 中的 for 循環已經比 C 語言更進了一步,通常不需要額外的狀態變量來記錄當前循環的次數,但有時候也不得不使用狀態變量,如上例中第二個循環中比較兩個列表的元素。函數式編程的一大特點就是儘量拋棄這種明顯循環遍歷的做法,而是把注意集中在解決問題本身,一如在現實中我們批改試卷時,只需要將兩組答案並列進行比較即可:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

然後再將所有正確題目的分數累加起來,即可:

from functools import reduce

reduced = reduce(lambda x, y: x + y[1][1], filtered, 0)

print(reduced)

以上是對一位學生的結果處理,接下來只需要對所有學生進行同樣的處理即可:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

上面的示例通過 zip/filter/reduce/map 等函數將數據處理的方法打包應用到數據上,實現了基本的函數式編程操作。但是如果你對函數式有更深入的瞭解,你就會發現上面的 cal 方法中使用了全局變量 QUIZE,這會導致在相同輸入的條件下,函數可能產生不同的輸出,這是 FP 的大忌,因此需要進行整改:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

如此藉助閉包(Closure)的方法,就可以維持純淨的 FP 模式啦!

函數式編程的一大優勢就是Immutable Data(數據不可變),就是不依賴於外部的數據,而且也不改變外部數據的值,這種思想可以大大減少我們代碼的Bug,而且函數式編程也支持我們像使用變量一樣使用函數。Python作為面嚮對象語言,也提供了對於函數式編程的支持,雖然並不是那麼純粹,而且也不支持尾遞歸優化。

1.lambda的使用

lambda即匿名函數,合理地使用lambda不僅可以減少我們的代碼量,而且也可以更好地描繪代碼邏輯,比如現在我們有下面這樣一個函數。

>>>deff(x):

...returnx+x

# 調用這個函數

>>>f(2)

4

這個函數如果我們用lamda改寫的話,只要一行代碼就夠了。# lambda後面的x表示lambda函數要接收的參數,x + x表示lambda函數所要返回的值

>>>f=lambdax:x+x

# 可以看到f現在也是一個函數對象

>>>f

>

# 調用lambda函數

>>>f(2)

4

python中map()函數

map(function, iterable)接收兩個參數,第一個參數代表的是接收一個函數,第二個參數代表的是接收一個iteralbe類型的對象,比如list。

map函數的原理是:

1.每次從iterable中取出一個參數

2.將這個參數傳遞給我們的函數

3.然後函數返回的值加入一個list(這種說法不準確,只是為了幫助大家理解,後面我會解釋)。等所有的iterable對象遍歷完,map就把這個list返回給我們的調用者。下面我們直接通過實例來了解一下map的用法。

python中reduce()函數

reduce()函數也是Python內置的一個高階函數。reduce()函數接收的參數和 map()類似,一個函數 f,一個list,但行為和 map()不同,reduce()傳入的函數 f 必須接收兩個參數,reduce()對list的每個元素反覆調用函數f,並返回最終結果值。

例如:

請利用recude()來求積:

輸入:[2, 4, 5, 7, 12]

輸出:245712的結果

def prod(x, y):

return x*y

print reduce(prod, [2, 4, 5, 7, 12])

輸出:3360

python中filter()函數

和map/reduce類似,filter(function, iterable)一次也接收兩個參數,一個參數是函數,另外一個參數是iterable對象,從名字也可以看出,filter用於過濾iterble對象,比如說list(列表)。

它的原理是每次從iterable對象中取出一個元素作用於我們的function,如果function返回True就保留該元素,如果返回False就刪除該元素。下面我們通過一個實例來看一下filter的用法。

定義一個函數,如果接收的字符s為空,那麼返回False,如果為非空,那麼返回True

>>>function=lambdas:sands.strip()

>>>iterable=['AJ',' ','Stussy','','CLOT','FCB',None]

>>>filter(function,iterable)

>>>list(filter(function,iterable))

['AJ','Stussy','CLOT','FCB']

python中自定義排序函數

Python內置的 sorted()函數可對list進行排序:

sorted([36, 5, 12, 9, 21])

輸出:[5, 9, 12, 21, 36]

但 sorted()也是一個高階函數,它可以接收一個比較函數來實現自定義排序,比較函數的定義是,傳入兩個待比較的元素 x, y,如果 x 應該排在 y 的前面,返回 -1,如果 x 應該排在 y 的後面,返回 1。如果 x 和 y 相等,返回 0。

因此,如果我們要實現倒序排序,只需要編寫一個reversed_cmp函數:

def reversed_cmp(x, y):

if x > y:

return -1

if x < y:

return 1

return 0

這樣,調用 sorted() 並傳入 reversed_cmp 就可以實現倒序排序:

sorted([36, 5, 12, 9, 21], reversed_cmp)

輸出:[36, 21, 12, 9, 5]

sorted()也可以對字符串進行排序,字符串默認按照ASCII大小來比較:

sorted(['bob', 'about', 'Zoo', 'Credit'])

輸出:['Credit', 'Zoo', 'about', 'bob']

'Zoo'排在'about'之前是因為'Z'的ASCII碼比'a'小。

示例:

對字符串排序時,有時候忽略大小寫排序更符合習慣。請利用sorted()高階函數,實現忽略大小寫排序的算法。

輸入:['bob', 'about', 'Zoo', 'Credit']

輸出:['about', 'bob', 'Credit', 'Zoo']

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

走火入魔

走火(fn.py)

也許看了上面的 FP 寫法,你還是覺得挺囉嗦的,並沒有達到你想象中的結果,這時候就需要呈上一款語法糖利器:fn.py!fn.py 封裝了一些常用的 FP 函數及語法糖,可以大大簡化你的代碼!

pip install fn

首先從剛剛的閉包開始,我們可以用更加 FP 的方法來解決這一問題,稱為柯里化,簡單來說就是允許接受多個參數的函數可以分次執行,每次只接受一個參數

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

應用到上面的 cal 方法中:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

在 FP 中數據通常被看作是一段數據流在一串函數的管道中傳遞,因此上面的reduce和filter其實可以合併:

reduce(lambda x, y: x + y[1][1], filter(lambda x: x[0] == x[1][0], zip(student.ans, quize)), 0)

雖然更簡略了,但是這樣會大大降低代碼的可讀性(這也是 FP 容易遭受批評的一點),為此 fn 提供了更高級的函數操作工具:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

入魔(Hy)

如果你覺得上面的代碼已經足夠魔性到看起來不像是 Python 語言了,然而一旦接受了這樣的語法設定感覺也還挺不錯的。如果你興沖沖地拿去給 Lisp 或 Haskell 程序員看,則一定會被無情地鄙視,於是你痛定思痛下定決心繼續挖掘 Python 函數式編程的奧妙,那麼恭喜你,組織歡迎你的加入:Hail Hydra!

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

哦不對,說漏了,是Hi Hy!

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

Hy 是基於 Python 的 Lisp 方言,可以與 Python 代碼進行完美互嵌(如果你更偏好 PyPy,同樣也有類似的Pixie),

除此之外你也可以把它當做一門獨立的語言來看待,它有自己的解釋器,可以當做獨立的腳本語言來使用:

pip install git+https://github.com/hylang/hy.git

首先來看一下它的基本用法,和 Python 一樣,安裝完之後可以通過 hy 命令進入 REPL 環境:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

或者當做命令行腳本運行:

#! /usr/bin/env hy

(print "I was going to code in Python syntax, but then I got Hy.")

保存為 awesome.hy:

chmod +x awesome.hy

./awesome.hy

接下來繼續以上面的問題為例,首先可以直接從 Python 代碼中導入:

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

如果覺得不放心,還可以直接調用最開始定義的方法將結果進行比較:

;; 假設最上面的 normal 方法保存在 fun.py 文件中

(import fun)

(.normal fun students quize)

他6年賺的錢已超過老爸一生的積蓄,誰說python階級已固化?

還有很多包括視頻我就不一一截圖了,需要這些資料的可以先關注小編,轉發評論,私信小編回覆006、008即可領取資料。誠信小編!!!


分享到:


相關文章: