[一點(diǎn)總結(jié)]函數(shù)式編程入門 -- Curry, Compose

準(zhǔn)備給TWU的同學(xué)做一個關(guān)于函數(shù)式的Session,昨天在準(zhǔn)備Slides,順手總結(jié)一下,有正在學(xué)習(xí)的小伙伴可以做一個參考莹弊。

What

Functional Programming(函數(shù)式編程)在概念上和Object Oriented Programming(面向?qū)ο缶幊?, Procedural Programming(過程化編程)類似, 是一種編程范式喊崖。
與OOP以對象為中心的理念不同假褪,F(xiàn)P將所有計算機(jī)的操作視為函數(shù)運(yùn)算拳喻,函數(shù)是操作的基本單位。函數(shù)擁有和基本類型一樣的地位迹辐,可以將一個變量賦值為函數(shù)(First class -- 一等公民)堪嫂,可以在函數(shù)的參數(shù)中傳遞函數(shù)(higher-order function -- 高階函數(shù))

Why

  1. 學(xué)習(xí)一點(diǎn)新的編程范式可以有效防止老年癡呆创千。
  2. 真的很有趣
  3. 相比于過程化缰雇、面向?qū)ο螅瘮?shù)式書寫的代碼更易讀追驴,更簡短械哟。
  4. 因?yàn)楹瘮?shù)式編程是無副作用(side effects)的,不需要考慮死鎖問題殿雪,適合并發(fā)編程暇咆,因此在云計算領(lǐng)域得到了廣泛應(yīng)用(Scala)

How

好了,進(jìn)入正題
以下示例代碼均為JavaScript

1. 副作用--Side Effects

先來看兩段代碼

//代碼片段1
let minium = 20;
const checkAge = (age)=> age >= minium;
//代碼片段2
let number = 2;
const multipleNumber = (n) => {
  number = number * n;
  return number;
}

這兩段代碼有問題嗎丙曙?
通常情況下爸业,代碼片段1并不會發(fā)生什么問題, 我們傳入年齡,并且判斷是不是大于20歲亏镰。

但如果有人修改了minium呢扯旷?此時判斷的條件改變了,導(dǎo)致我們的結(jié)果也會改變索抓。當(dāng)我們第二次運(yùn)行checkAge(22)的時候钧忽,可能返回的并不是第一次運(yùn)行的結(jié)果。

對于checkAge這個函數(shù)來說逼肯,它需要觀測的值不僅有入?yún)?code>age,還有一個全局變量minium,它的運(yùn)行結(jié)果依賴系統(tǒng)狀態(tài)惰瓜,這對于程序員來說是十分痛苦的。

而代碼片段2就很容易發(fā)現(xiàn)問題了汉矿,這個函數(shù)修改了一個全局變量,換言之备禀,它修改了系統(tǒng)狀態(tài)洲拇,當(dāng)?shù)诙屋斎胂嗤瑓?shù)的時候你會得到一個不一樣的結(jié)果。

不曲尸,這太讓人難過了赋续,這不是我們想要的,我們希望我們的函數(shù)足夠純凈另患,相同的輸入永遠(yuǎn)得到相同的輸出纽乱。而且,不要做多余的事:
偷偷在console里打一個log
偷偷給某個api發(fā)送一個request
偷偷修改本地文件系統(tǒng)

2. 純函數(shù)--Pure Function

Side Effects好嗎昆箕?我們心里都知道不好鸦列,但有的時候你不得不接受它租冠,就像生活。
或許你的系統(tǒng)需要維護(hù)某些狀態(tài)來運(yùn)行不同的程序薯嗤,和它們接觸的函數(shù)就不可避免的變得不純凈起來顽爹。但這并不意味著我們就放棄治療了,把怪獸關(guān)在壁櫥里總比把它放出來到處搞破壞來得好骆姐。正所謂關(guān)注分離镜粤。

是時候引入純函數(shù)了。
我們把上面的代碼修改成這樣如何:

//代碼片段3
const checkAge = (age) => { const minium = 20; return age >= 20;}
//代碼片段4
const multipleNumber = (number, n) => {return number * n};

對于代碼片段3來說玻褪,現(xiàn)在它終于不用始終看著minium了肉渴,現(xiàn)在minium變成了它的一部分,永遠(yuǎn)不會改變带射。這樣我們相同的輸入同规,永遠(yuǎn)都會有相同的輸出。

對于代碼片段4庸诱,我們可能會有疑問:“原來我只用輸入一個參數(shù)捻浦,現(xiàn)在我要輸入兩個參數(shù)了,有點(diǎn)不太對勁扒潘朱灿?”
“Emmmmm, 至少我們可以通過始終調(diào)用multipleNumber(2,n)或者給number一個默認(rèn)值來讓它變得純函數(shù)起來const multipleNumber(n,number=2)

所以純函數(shù)是什么呢?
Stateless(不改變狀態(tài)钠四,也不被狀態(tài)影響)

3. 柯里化 -- Curry

讓我們稍稍改變一下代碼片段4盗扒,這次讓我們消除疑慮。

//代碼片段5
const multipleNumber = (number) => (n) => {return number * n}

“教練缀去,有什么區(qū)別奥略睢?我還是得傳兩個參數(shù)啊缕碎,唯一的區(qū)別是我得寫兩對括號了啊(multipleNumber(2)(n)H煊啊!咏雌!”

哎呀別急凡怎,回頭看看文章開頭,"函數(shù)擁有和基本類型一樣的地位赊抖,可以將一個變量賦值為函數(shù)(First class -- 一等公民)统倒,可以在函數(shù)的參數(shù)中傳遞函數(shù)(higher-order function -- 高階函數(shù))。"

再看看代碼片段5氛雪,看到兩個=>有沒有想起什么房匆?

我們有一整頁的時間思考
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……
……

答案是:
我們現(xiàn)在可以寫這樣的代碼了:

const multipleTwo = multipleNumber(2); // (n) => {return 2*n}
const result = multipleTwo(3);//6

我們把這樣拆分函數(shù)參數(shù) --> 形成一個高階函數(shù) 的過程,稱為柯里化。
這就是一個最簡單的柯里化的例子浴鸿。
好處井氢?

生成的高階函數(shù)變成了一個工廠,用來生產(chǎn)諸如multipleTwo這樣的函數(shù)赚楚。
有效消除重復(fù)代碼毙沾,誰用誰知道。

再舉一個例子:

const matchRegex = (regex, str) => str.match(regex);

將其柯里化之后得到一個叫matchRegex的工廠

const matchRegex = (regex) => (str) => str.match(regex);

我們可以生產(chǎn)這樣的函數(shù):

const hasSpace = match(/\s+\g);

對宠页,matchRegex是沒有上下文含義的左胞,但生成的函數(shù)根據(jù)傳入的正則產(chǎn)生了不同的業(yè)務(wù)含義。

*只傳給函數(shù)一部分參數(shù)也叫局部調(diào)用举户,當(dāng)然 和柯里化是完全不同的兩個東西

4. 組合 -- Compose

現(xiàn)在相信我們已經(jīng)對函數(shù)式編程有了一個初步的認(rèn)知烤宙。

想象這樣一個業(yè)務(wù)場景:
我們是Email Writer,
有時候我們需要把郵件的某些Text變成UPPERCASE,
const uppercase = (str) => str.toUpperCase();
有時候我們需要給Text末尾加上一個感嘆號,!
const addBondToEnd = (str) => str+"!";
有時候我們需要既把Text變成uppercase, 也要在后面加一個感嘆號:TEXT俭嘁!

怎么辦呢躺枕?

//So Easy
const addBondToEndAndUpperCase=(str)=>{
  return addBondToEnd(uppercase(str));
};

把兩個函數(shù)都調(diào)用一遍不就好了。我們還生成了一個新函數(shù)供填,每次想做這兩個操作的時候調(diào)用這個新的函數(shù)就行了拐云。

真棒!我們已經(jīng)知道組合是什么東西了近她!

但多想一步叉瘩,如果我們有許許多多這樣的基礎(chǔ)函數(shù)大概10000個,我們想要生成不同的組合函數(shù)粘捎,我們還要這樣手寫新函數(shù)嗎薇缅?

我太懶了,我不想這么做攒磨,重復(fù)寫這樣的code讓我覺得生不如死泳桦。

讓我們回到文章開頭,"函數(shù)擁有和基本類型一樣的地位娩缰,可以將一個變量賦值為函數(shù)(First class -- 一等公民)灸撰,可以在函數(shù)的參數(shù)中傳遞函數(shù)(higher-order function -- 高階函數(shù))。"

這次直接揭曉答案吧:
對于我這樣的懶人拼坎,需要的是這樣一個工具

const compose = (f, g) => (x) => f(g(x));

這樣我就可以這樣調(diào)用了:

const uppercase = (str) => str.toUpperCase();
const addBondToEnd = (str) => str+"!";
//addBondToEndAndUpperCase
compose(addBondToEnd, uppercase)("text");//TEXT浮毯!
//Of course you can assign it to a variable
const addBondToEndAndUpperCase = compose(addBondToEnd, uppercase)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市演痒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趋惨,老刑警劉巖鸟顺,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡讯嫂,警方通過查閱死者的電腦和手機(jī)蹦锋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欧芽,“玉大人莉掂,你說我怎么就攤上這事∏樱” “怎么了憎妙?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長曲楚。 經(jīng)常有香客問我厘唾,道長,這世上最難降的妖魔是什么龙誊? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任抚垃,我火速辦了婚禮,結(jié)果婚禮上趟大,老公的妹妹穿的比我還像新娘鹤树。我一直安慰自己,他們只是感情好逊朽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布罕伯。 她就那樣靜靜地躺著,像睡著了一般惋耙。 火紅的嫁衣襯著肌膚如雪捣炬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天绽榛,我揣著相機(jī)與錄音湿酸,去河邊找鬼。 笑死灭美,一個胖子當(dāng)著我的面吹牛推溃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播届腐,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼铁坎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了犁苏?” 一聲冷哼從身側(cè)響起硬萍,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎围详,沒想到半個月后朴乖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祖屏,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年买羞,在試婚紗的時候發(fā)現(xiàn)自己被綠了袁勺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡畜普,死狀恐怖期丰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吃挑,我是刑警寧澤钝荡,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站儒鹿,受9級特大地震影響化撕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜约炎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一植阴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧圾浅,春花似錦掠手、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至灸拍,卻和暖如春做祝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸡岗。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工混槐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轩性。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓声登,卻偏偏與公主長得像,于是被迫代替她去往敵國和親揣苏。 傳聞我的和親對象是個殘疾皇子悯嗓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容