函數(shù)式編程的理念

1. FP 理念

1.1 不變性

  • 沒有變量的概念, 只有'值'.
    • 避免改變狀態(tài)及可變數(shù)據(jù).
    • 三部曲: 編寫函數(shù), 使用REPL工具測(cè)試, 使用.

1.2 聲明性風(fēng)格

  • 代碼是描述期望結(jié)果的表達(dá)式.
    • 將表達(dá)式組合起來, 可以同時(shí)隱藏執(zhí)行細(xì)節(jié)和復(fù)雜性.
  • 聲明性只提出what to do 而不是解決how to do.
    • 例如, 使用foreach 來遍歷集合數(shù)據(jù), 其實(shí)是在重復(fù)how to do(命令式).
    • 提取通用的'思想'為詞語(函數(shù)名), 已經(jīng)是what to do 了.
// csharp
return sourceList.Where(item => item %2 == 0);
// or LINQ 
stylereturn from item in sourceList where item % 2 == 0 select item;
// if we have a function(abstract) already. 
return sourceList.Where(NumberIsEven);
  • C#的LINQ借鑒的是FP的高階函數(shù)以及monad腾务,只是和SQL長(zhǎng)的比較像而已(可能是為了避免學(xué)習(xí)成本)杆兵。

1.3 類型

  • 每個(gè)表達(dá)式都有對(duì)應(yīng)的類型, 這確保了表達(dá)式組合的正確性.
  • 泛型是一種代碼重用技術(shù).
    • 使用類型占位符來將真正的類型延遲到運(yùn)行時(shí)再?zèng)Q定.
    • 類型模板, 在需要時(shí)插入真實(shí)的類型.
    • 包裝: 打包了某種具體類型.
      • 包裝類: int?其實(shí)是指Nullable<T>對(duì)int類型的包裝详幽。

1.4 表達(dá)式求值

  • 整個(gè)程序就是一個(gè)大的表達(dá)式.
    • 關(guān)心的是表達(dá)式的副作用, 而不是其返回值.
    • 沒有語句的概念.

2. 高階函數(shù)

  • 方便函數(shù)的定制.
    • 隱藏具體的執(zhí)行細(xì)節(jié), 將可定制的部分(行為)抽象處理傳遞給某個(gè)高階函數(shù)使用.
    • 類似OO 的模板方法.
  • 提升
    ('a -> 'b) -> M<'a> -> M<'b>
    lift2: ('a -> 'b -> 'c) -> M<'a> -> M<'b> -> M<'c>
    • 允許將一個(gè)對(duì)值進(jìn)行處理的函數(shù)轉(zhuǎn)換為一個(gè)在不同設(shè)置中完成相同任務(wù)的函數(shù).

3. 柯里化(Currying) 和部分函數(shù)應(yīng)用

  • 柯里化(Currying)
function addBy(value) 
{ 
  return function(n) {  
    // 將value 的進(jìn)行閉包處理
    return n + value; 
  };
}
var add10 = addBy(10);
var result11 = add10(1);

// orElse
var result11 = addBy(10)(1);
  • 多個(gè)參數(shù)時(shí):
    let fakeAddFn n1 = fun n2 -> fun n3 -> fun n4 -> n1 + n2 + n3 + n4

4. 遞歸及優(yōu)化

  • 尾遞歸優(yōu)化:
    • 當(dāng)在遞歸調(diào)用的時(shí)候,不需要做任何工作, 則可以從當(dāng)前的調(diào)用棧直接跳到下一次的調(diào)用棧上去.
    • 關(guān)鍵是對(duì)累加器的使用.
    • 當(dāng)需要處理非常龐大的列表時(shí).
public int Factorial(int n) {
    return n <= ? 1 : n * Factorial(n - 1);
}
>> 每次遞歸都卡在了n*_ 上, 必須等后面的結(jié)果返回后,當(dāng)前函數(shù)的調(diào)用棧才能返回.
n               (n-1)       ...      3         2       1  // state
--------------------------------------------------------
n*f(n-1) -> (n-1)*f(n-2) -> ... -> 3*f(2) -> 2*f(1) -> 1  // stack in
                                                       |  
n*r      <-  (n-1)*(r-1) <- ... <-   3*2  <-   2*1  <- 1  // stack out

private int FactorialHelper(acc, n) {
    return n <= 1 ? acc : FactorialHelper(acc * n, n - 1);
}
public int Factorial(int n) { return FactorialHelper(1, n); }

init        f(1, n)             // stack in
                |               // stack pop, jump to next
n           f(n, n-1)           // stack in
                |               // stack pop, jump to next
n-1         f(n*(n-1), n-2)     // stack in
                |               // stack pop, jump to next
...         ...                 // stack in
                |               // stack pop, jump to next
2           f((k-1), 1)         // stack in
                |               // stack pop, jump to next
1           k                   // return result
  • fold 自帶累加器的化簡(jiǎn)函數(shù), 抽取了遍歷并化簡(jiǎn)核心的步驟, 僅將需要自定義的部分以參數(shù)的形式放出來.
    • fold 的累加器需要指定一個(gè)初始值, 而reduce 的初始累加器使用列表的第一個(gè)值.

5. 記憶化

  • FP 函數(shù)是沒有副作用的
    • 也就意味著以相同的參數(shù)調(diào)用同一函數(shù)將會(huì)返回相同的結(jié)果.
    • 對(duì)于一些會(huì)被多次調(diào)用的函數(shù), 將對(duì)應(yīng)參數(shù)的求值結(jié)果緩存起來, 以后遇到相同的參數(shù)直接返回緩存結(jié)果.
  • 通用的記憶化函數(shù)
let memorize f =
    let cache = new Dictionary<_, _>()
    fun p ->
        match cache.TryGetValue(p) with
        | true, result -> result
        | _ ->
            let result = f p
            cache.Add(p, result)
            result

6. 惰性求值

  • 立刻求值: 將表達(dá)式n % 2 == 0 ? "right" : "wrong"綁定到標(biāo)識(shí)(即變量名)isEven上
  • 將isEven 綁定到某種結(jié)構(gòu)上, 結(jié)構(gòu)知道如何求值,并且是按需求求值.
    • var isEven = new Lazy<string> (() => n % 2 == 0 ? "right" : "wrong");
    • 使用isEven.value 來即可求值并拿到結(jié)果.
  • 通過函數(shù)础爬,函數(shù)表達(dá)了某種可以得到值的方式,但是需要調(diào)用才能得到.
    • var isEven = (Func<string>)(() => n % 2 == 0 ? "right" : "wrong");
    • 通過函數(shù)調(diào)用isEven() 來立刻獲取值.

7. 延遲

  • 回調(diào)函數(shù): 延后了某種行為, 且該行為對(duì)上下文有依賴.
  • 手段: 在函數(shù)調(diào)用結(jié)束后自動(dòng)調(diào)用指定的行為(函數(shù)).
    • 在進(jìn)行尾遞歸優(yōu)化時(shí), 之前累加器累加的值, 延遲使得可以累加行為.
  • 延遲可以推導(dǎo)出bind: 外層匹配模式.
// binding:
('a -> 'b)    -> 'a -> 'b
// map:
('a -> 'b)    -> M<'a> -> M<'b>
// bind:
('a -> M<'b>) -> M<'a> -> M<'b>

10. Mics

  • 一等用來描述'值', 函數(shù)被看做一等公民, 就是函數(shù)等價(jià)于'值'的概念.
  • 高階函數(shù): 以一個(gè)函數(shù)(而不是值)作為參數(shù); 返回一個(gè)函數(shù)作為結(jié)果.
  • 編程方式
    • 命令式編程: 建立在直接操作和檢查程序狀態(tài)之上.
      • 局限于細(xì)節(jié).
      • 函數(shù)式的思路是將程序拆分并抽象成多個(gè)函數(shù)再組裝回去.
    • 面向?qū)ο缶幊?
      • js 是利用現(xiàn)有的對(duì)象作為原型來生產(chǎn)特定的實(shí)例.
      • 必須有一個(gè)語義的自引用(this).
    • 元編程
      • 可以通過編寫代碼來改變某些代碼被解釋的方式.
      • this 引用的動(dòng)態(tài)性質(zhì)可以用來元編程.
  • 集合中心編程.
    • 用100 個(gè)函數(shù)操作一個(gè)數(shù)據(jù)結(jié)構(gòu), 比用10 個(gè)函數(shù)操作10 個(gè)數(shù)據(jù)結(jié)構(gòu)要好.
  • 閉包的兩個(gè)重點(diǎn)
    • 閉包會(huì)捕獲一個(gè)值(或引用), 并多次返回相同的值.
    • 每個(gè)新的閉包都會(huì)捕獲不一樣的值.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壮吩,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件加缘,死亡現(xiàn)場(chǎng)離奇詭異鸭叙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拣宏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門沈贝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勋乾,你說我怎么就攤上這事宋下。” “怎么了辑莫?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵学歧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我各吨,道長(zhǎng)枝笨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮横浑,結(jié)果婚禮上剔桨,老公的妹妹穿的比我還像新娘。我一直安慰自己徙融,他們只是感情好洒缀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欺冀,像睡著了一般树绩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脚猾,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天葱峡,我揣著相機(jī)與錄音,去河邊找鬼龙助。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蛛芥,可吹牛的內(nèi)容都是我干的提鸟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仅淑,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼称勋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涯竟,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤赡鲜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后庐船,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體银酬,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年筐钟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了揩瞪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡篓冲,死狀恐怖李破,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情壹将,我是刑警寧澤嗤攻,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站诽俯,受9級(jí)特大地震影響妇菱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一恶耽、第九天 我趴在偏房一處隱蔽的房頂上張望密任。 院中可真熱鬧,春花似錦偷俭、人聲如沸浪讳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淹遵。三九已至,卻和暖如春负溪,著一層夾襖步出監(jiān)牢的瞬間透揣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工川抡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辐真,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓崖堤,卻偏偏與公主長(zhǎng)得像侍咱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子密幔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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