標籤(空格分隔): 函數語言程式設計 函子 functor
很多前端在學習函數語言程式設計之前,都會被各種概念折磨的死去活來,本文的重點算是函數語言程式設計之前的一個甜品,重點在如何切入。
函子即Functor是FP(函數語言程式設計簡寫)當中重要的概念,理解這個概念對你學習FP後面的很重要。不然學的就是雲裡霧裡。
網上的文章不在少數,很多人也看過,筆者非科班出身,僅從個人角度學習和科普這個知識點
送給川普的豬頭肉
有這麼一個場景,可以想象下,我們和鄰居之間交換東西,都很直接,不需要繁瑣的包裝等等。就像加減法一樣,1 + 1 = 2;假如我要給在美國的親戚川普寄送一些豬頭肉。很顯然我不能拿著豬頭肉直接去。
我首先會把豬頭肉包裝,那麼他就是被容器化之後的肉,交給快遞員,我會告訴快遞員這個裝有肉的容器開啟方法,因為通過海關的時候需要開啟進行檢疫,然後蓋上郵戳,重新包裝,送往美國白宮。
以上例子並不是肉自己直接走過去的,而是容器化之後的肉,有包裝的方法,比如易碎、保險、向上開啟等等注意事項,防止海關檢查的時候不小心損壞。
我們有用程式碼實現下
//肉盒子
class MeatBox {
constructor(meat) {
this.value = meat;
}
//map為開啟包裝的方法
map(fn) {
return new MeatBox(fn(this.meat));
}
}
複製程式碼
海關開啟之後檢疫完成,他又會根據盒子的規範重新打包成新的肉容器,方便在美國海關檢疫。同樣美國海關覺得豬頭肉和好吃,咬一口,又打包成原來的樣子,給川普總統。
繼續看程式碼流程
//把肉容器化
let meatBox1 = new MeatBox('豬頭肉');
//海關檢疫
let meatBox2 = meatBox.map(function(meat){
return check(meat);
});
//美國海關咬了一口
let meatBox3 = meatBox.map(function(meat){
return eat(meat);
});
//川普吃到了你的豬頭肉
meatBox3.map(function(meat){
return Trump(meat);
});
複製程式碼
用鏈式寫法
new MeatBox('豬頭肉').map(check).map(eat).map(function(trump);
複製程式碼
如果白宮在你家隔壁,你直接送過去就行,沒這麼多事。但是很明顯不行。我們總結一下上面幾個特點
- 肉從一個單體或者一個值被容器化了,變成了一個具有資料型別的容器
- 每一次對肉的都會拿出來進行計算然後又重新根據規則或者協議標準容器化;
- 容器具有map這個方法,來取值,並且返回的也有map方法;
- 還可以鏈式呼叫;
- 像我們學習資料時候的對映 y = f(x),包含了值和變形關係;
什麼是函子呢(Functor)?
根據以上的場景,得出
- Functor(函子)遵守一些特定規則的容器型別或者資料程式設計協議;
- 具有一個通用的map方法,返回新例項,這個例項和之前例項有相同的規則;
- 具有結合外部的運算能力;
按照我的理解,函子Functor其實準確的來說是:值被容器化之後具有一條標準協議規範的資料型別或者資料容器。map屬於函子的一個特徵,Monad(單子),這個概念後面講,肯定還有跟多的單子。
如果單純的說具有map的資料型別是函子,沒錯但是不嚴謹。比如引用型別資料,很多文章也認為是函子,因為也具有map方法。
其實在阮一峰的文章當中說的很清楚,我就不再闡述,有興趣的可以看看。
Functor 是一個對於函式呼叫的抽象,賦予容器自己去呼叫函式的能力。把東西裝進一個容器,只留出一個介面 map 給容器外的函式,map 一個函式時,我們讓容器自己來執行這個函式,這樣容器就可以自由地選擇何時何地如何操作這個函式,以致於擁有惰性求值、錯誤處理、非同步呼叫等等非常牛掰的特性。
說了這麼多,應用場景呢?
其實從上面的場景看,你可能會說,我直接去白宮不就完了,省的川普說為什麼有人送了一塊咬過得的肉。
在程式設計開發中,尤其是多人協作,一個資料從資料庫出來,會各種計算、加入業務邏輯,最終呈現給消費方。資料鏈路越長,資料元資訊越容易丟失。就好像一句話通過幾個人之後,完全和原來的意思相差甚遠。
以上只是一方面,更多是融合在函數語言程式設計當中,讓資料的計算和業務剝離,我們不用大量的業務邏輯和指令式程式設計,例如for迴圈,把重點放在副作用上的等(副作用在這裡是中性詞);資料鏈路會更容易清晰去實現。
這就是函子的基本概念,其實也沒那麼複雜,理解這個對你函數語言程式設計有很大的幫助。
有興趣的可以關注筆者QQ群:126274877