何為高階函數
大家可能對這個名詞並不熟悉,但是這個名詞所表達的事物卻是我們經常使用到的。只要我們的函數的參數能夠接收函數,或者函數能夠返回函數,當然動態生成的也包括在內。那么我們就將這類函數叫做高階函數。但是今天我們的標題並不是高階函數,而是標准高階函數,既然加上了這個標准,就意味着在函數式編程中有一套標准的函數,便於我們每次調用。而今天我們將會介紹三個標准函數,分別為Map、Filter、Fold。
Map
這個函數的作用就是將列表中的每項從A類型轉換到B類型,並形成一個新的類型。下面我們可以看看在FCSLib中是如何實現的:
相信很多人應該都能夠看懂這段代碼,僅僅只是循環list數據。然后調用convert函數轉換,最后通過yield將其組成一個列表返回。下面我們通過一個簡單的例子來演示下如何使用這個標准高階函數:
我們通過聲明了一個數組data,然后利用map函數將其中的ID提取出來,單獨形成一個列表。
Filter
通過名字應該能夠猜出他大概的含義了,就是用來過濾數據的。相信一些人能夠根據上面寫出這個標准高階函數的具體實現,當然這里不管讀者有沒有寫出,我們都來看看FCSLib中是如何實現的:
這里我們只是少了一個泛型參數,對應的第一參數的是能夠接收T類型返回bool類型的函數(Predicate也是.NET自帶的,可能我們用慣了Func和Action,並且利用Func<T,bool>也可以實現一樣的效果。),而語句主要是在循環后調用了predicate去判斷是否滿足條件,如果滿足條件則返回。
Fold
這個標准高階函數可能會比較難,因為它分為了左折疊和右折疊兩種。接收的參數有三個,分別是起始值,累加函數,列表。大致的執行過程就是在第一次調用我們的累加函數時將起始值作為一個參數,而列表中的第一個值作為第二個參數去調用我們的累加函數,累加函數計算出結果,而這個結果將作為下一次調用累加函數時的第一參數,而第二個參數則是列表中的下一項,以此類推。所以通過這個高階函數我們可以直接計算出列表中每項數據累加后的結果,而在FCSLib中的具體實現則如下所示:
通過查看源代碼我們也可以得出之前的執行流程,在第一次的時候v的值為startValue,而之后就會將accmulator函數的返回值保存進v,這樣下一次執行accmulator時就作為第一個參數傳入。下面我們通過一個簡單的例子來看看如何使用:
左折疊完成之后對應的還有一個右折疊,當然根據名字也可以猜出。就是從list的末尾開始進行循環,所以我們可以這樣來實現FoldR:
這里還要指出的就是左折疊和右折疊是最常用的算法之一,他完全可以實現像我們之前介紹過的Map和Filter,而只要按照下面這種方式寫即可:
這里我們就是利用Fold函數的一個特性來實現的,因為累加函數每次都會將上一次計算得出的結果傳遞給下一個累加函數(new List<R>()我們直接將其作為起始值傳入),最后我們都會通過return x將這個列表返回,這樣下一次再調用這個累加函數之后x依然是同一個列表。對應的還有Filter的實現:
而具體的調用方式則和Map,Filter是一致的,並沒有什么特別大的區別。
關於Linq中的Map、Filter和Fold
其實函數式編程已經在不知不覺中慢慢的進入到我們的生活中,其實我們經常在Linq中使用的select、where和aggregate就是對應着函數式編程中的map、filter和fold。