初探函數語言程式設計

發表於2017-02-16

前言

繼續向下看廖大教程,看到了函數語言程式設計這一節,當時是覺得沒啥用直接跳過了,這次準備要仔細看一遍了,並記錄下一些心得。

函數語言程式設計

上學期有上一門叫 ‘人工智慧’ 的課,老師強行要我們學了一個叫做 prolog 的語言,哇那感覺確實難受,思維方式完全和之前學過的不一樣,寫個漢諾塔想了半天,最後還是在網上找了段程式碼修改一下(怕被老師發現抄襲)才寫出來,貼一段出來感受一下:

當時是差不多弄懂了,主要是資料實在太少,debug 都無從談起,一遇上 bug 就 gg,我現在自己看也有點頭暈。不過據說 prolog 當年能和 Lisp 一爭高下,最近對 Lisp 也有點興趣,等弄完這些就去參拜一下這類函式式語言。

何謂函數語言程式設計?廖大這裡寫道:

函數語言程式設計就是一種抽象程度很高的程式設計正規化,純粹的函數語言程式設計語言編寫的函式沒有變數,因此,任意一個函式,只要輸入是確定的,輸出就是確定的,這種純函式我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函式內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函式是有副作用的。

可能看完還是有些不太理解,不急,先看完這幾個小節吧。

高階函式

在數學和電腦科學中,高階函式是至少滿足下列一個條件的函式:

  • 接受一個或多個函式作為輸入
  • 輸出一個函式

也就是說,把函式本身當成引數傳遞,或者返回一個函式。

例如,可以像普通賦值一樣將函式賦值給變數:

也可以給函式賦值(程式碼接上):

還可以傳參,例如,一個計算所有數字的和的函式:

當然,將這個 f 換成乘法就是計算所有數字的乘積了。

再來看看 python 內建的一些高階函式,經常會用到。

map/reduce

記得上學期上雲端計算的課程時依稀有聽到過這個詞,不過這課很水,就沒怎麼聽,在這裡看到好像發現不太一樣??

不過沒啥說的,簡單說一下每個函式的作用。

對於 map,其計算式可以看成這樣:

對於 reduce,其計算式可以看成這樣:

廖大那裡說得很清楚啦。

filter

filter 和 map 函式類似,接受一個函式和 iterable,返回也是一個 list,不過其功能是根據函式返回值是否為 True 來判斷是否保留該值。例如:

sorted

sorted 函式同樣是一個高階函式,對引數 key 傳遞函式可以將需要排列的序列經過 key 函式處理後再進行排序,不過不會改變序列的值,例如:

裝飾器(decorator)

匿名函式就不說了,以後用時再仔細看吧,裝飾器我記得之前看 flask 的時候都研究了好久,這次再來複習一下。

簡單裝飾器

首先是一個簡單的裝飾器,在每次呼叫函式前列印出日誌:

這就是一個極其簡單的裝飾器,如何使用它呢?我最先看到的用法是在需要裝飾的函式前新增@,但其實這是 Python 的一個語法糖,最原始的用法反而更能讓人理解,先定義一個函式 f:

這樣定義了之後,我們再呼叫 f 函式:

使用 @log 的結果與其一樣,其實@符號作為裝飾器的語法糖,與前面的賦值語句具有相同的功能,使程式碼看起來更簡潔明瞭,避免再一次賦值操作,就像下面這樣:

含引數的裝飾器

有時候我們還需要向裝飾器中傳入引數,例如,狀態,層次等資訊,只需要在 wrapper 函式外再’包裹’一層函式,如下所示:

進一步理解

為了再進一步理解裝飾器,我們可以列印出函式 f 的 name 屬性:

聯絡到最前面的裝飾器賦值語句,就可以大致明白髮生了什麼:f = log(f) 使得 f 指向修改為 log(f) 的返回值,即 wrapper 函式。每次執行原函式 f 時,則會呼叫 wrapper 函式,在我們這個例子中,則是先列印日誌,然後執行原函式 f。

不過這樣有一個問題,這樣使得原函式 f 的元資訊被替換了,關於 f 的許多資訊消失不見,這是很難令人接受的,不過好在我們有 functools 模組,修改函式為:

另外,還可以對同一個函式新增多個裝飾器:

總結

關於函數語言程式設計我也不是很瞭解,這裡只是大概瞭解了一下其概念吧,平時肯定還是使用指令式程式設計用得多。不過有語言是純函式式語言,例如 Haskell 或 Lisp,學習它們會使得人開啟一種新思路。

以上~

相關文章