關於函數式編程的思考(1)

做者:李英傑,美團金融前端團隊成員。歡迎你們一塊兒來探討FPjavascript

題外話:只是單純地談談我的對函數式編程的理解,歡迎你們來一塊兒探討。也不會說起高階函數與範疇學的內容,只聊一些很入門的問題。函數式編程的優勢這裏也不作過多說明,會推薦你們看幾篇文章,裏面有很好的闡述。

斜體灰字部分是一些我的的吐槽和私貨

目錄

  1. 爲何函數式編程在前端復興?前端

  2. 什麼是函數式編程?java

  3. 函數式編程如何思考問題?react

  4. 函數式編程與面向對象編程有什麼區別,各自的優缺點是什麼?ajax

  5. map,reduce是函數式編程嗎?編程

  6. 推薦redux


1.咱們來簡單捋捋前端的發展。

  • 上古時代,所有都是靜態頁面
  • 有js了,你們能夠在頁面搞一搞簡單的交互了
  • ajax來了,能夠更加自由地作本身想作的事情了
  • 切圖仔們不甘寂寞了,要搞事情,因而有了SPA後端

    好,咱們先停在這裏了。SPA來了,咱們很開心,曾經的小人物能夠嘗試着作主人了,後端只要給接口咱們就能呈現出一個完整的網站了。然而沒過多久咱們開始不開心了,能力越大責任越大,須要考慮的問題愈來愈多,問題也愈來愈複雜。其中一個比較關鍵的問題就是:數據與展現之間的關係咱們該如何處理?數組

按時間順序,列幾個我瞭解的SPA框架框架

Extjs --- MVC
Angular --- MVVM
React/Redux --- 單向數據流(好尷尬,別人都是英文字母)
各類MVW框架都在努力地幫助咱們理清數據與展現之間的關係。

發展到今天,展現方面React很流行了;數據處理方面,flux單向數據流的思想獲得了你們的認同,基於flux思想的redux目前基本成爲了複雜數據場景中react的標配。

不管是React仍是Redux,都或多或少提到了函數式編程。是它們參考了函數式編程,仍是在提出方案後發現與函數式編程比較搭,咱們先不深究。能夠看到的是,前端當前的發展階段在必定程度契合了函數式編程的思惟。
前端都是一羣愛折騰的人,比較喜歡創造或建造一些東西。因此咱們可能不是很喜歡大而全的東西,而是喜歡搭積木。
React說:我只負責view,你能夠自由選擇處理data的夥伴
Redux說:我只負責data,你能夠自由選擇處理view的夥伴
因而——沒法抗拒
而它們提到的函數式編程,又像是一個已經半開的寶藏大門,在勾引着咱們進去看看。必須認可,我也是看Redux入坑的。據我所知,像這樣入坑的前端還很多。

函數式編程並非新的編程範式,只不過最近才走進了前端的視野。你們能夠思考一下這個問題,特別是對其感興趣的同窗——爲何直到今天前端纔想要了解函數式編程,函數式編程是否是在全部場景下適用?
咱們能夠憑着興趣和激情在全部場景下進行嘗試,不過針對不一樣的場景選擇合理的解決方案纔是咱們應該有的態度。好比:Redux做者也會說一句:或許你不須要Redux。

2.介紹完背景,能夠正式發問了:什麼是函數式編程?

前端方向上介紹函數式編程的文章和書籍對於定義給的都比較晦澀,或者乾脆略過不寫,只是寫了些函數式編程的特色和優勢。我的認爲這對於前端同窗理解函數式編程形成了必定的困擾。
最初學習的時候我不能理解:
    爲何相同輸入對應同一輸出
    爲何輸入的參數不能改變
    爲何沒有循環,只有遞歸
    爲何提到函數式編程就提數學
他們只是告訴我這個東西就該是這樣子的。

在解釋以上問題以前,先拋出另外一個問題——什麼是函數?
貌似是一個很白癡的問題,碼農們怎麼會不知道什麼是函數,每天都在寫函數的好很差。

再明確一下這個問題,「函數式編程」裏的「函數」是什麼?
這裏面提到的「函數」是數學中的函數,不是編程中的函數。
數學中的函數是這樣定義的:函數在數學中爲兩集合間的一種對應關係:輸入值集合中的每項元素皆能對應惟一一項輸出值集合中的元素。——選自維基百科

因此「函數」是這個樣子的


也會是這個樣子的

但不是這個樣子的

function test(x, y) {
  console.log(x, y);
}複製代碼

這是碼農們的函數 :)

瞭解了這一點,結合以上兩個函數的例子,你們能夠思考一下剛剛提到的問題,應該很快就能夠理解了。
    爲何相同輸入對應同一輸出
    爲何輸入的參數不能改變
    爲何沒有循環,只有遞歸
    爲何提到函數式編程就提數學

3.瞭解什麼是函數編程,接下來就是咱們如何用函數式編程解決問題了

  • 將實際問題轉換爲數學問題
  • 小函數組合成大函數,大函數組成更大的函數,直至解決問題 —— 這裏函數還是數學中的函數

用數學去解決問題有幾點好處

  • 保證穩定的手段之一 ———— 數學是穩定的,關於數學爲何是穩定的,這裏就不作說明了:)
  • 可推導 ———— 數學函數運算

推導在咱們實際編程中有什麼意義?
來看一個例子(這個例子是在其餘書籍中看到的,只不過書籍的做者從其餘角度分析了這個例子)

async({
  success: data => handleData(data),
  fail: () => alert('error)
})複製代碼

async是一個異步函數,success和fail分別對應了該異步函數在成功和失敗狀況下的處理
從函數式的角度來思考一下如何優化
首先將程序中的函數用數學中的函數表示出來

而後咱們來根據函數的內容來推導一下

若是對於任意輸入,函數f, g的輸出都一致,那麼f,g就是能夠互相替換的。

對於這個例子來講,咱們就能夠寫成這個樣子

async({
  success: handleData,
  fail: () => alert('error)
})複製代碼

再舉一個例子,你們很是熟悉的小學應用題
應用題:
小雨帶着n個蘋果出去玩,路過薛大叔家,薛大叔給了她n個蘋果,薛大嬸給了她1個蘋果
問題1:小雨如今有多少個蘋果
路過張磚頭家,小雨給了張磚頭n個蘋果
問題2:小雨如今有多少個蘋果?
已知:
add(x, y) = x + y;
sub(x, y) = x - y;

能夠很快速地給出答案
問題1:f(x) = add(add(x,x),1)

let n = 10;
let result = add(add(n, 1), 1);複製代碼

問題2:g(x) = sub(f(x), x)

let result2 = sub(result, n);複製代碼

實際中,咱們只是想知道問題2的結果。
問題1的存在可能有兩個緣由

  • 咱們分解了開發過程
  • 最初咱們想要獲得問題1的結果,隨着業務的發展,如今咱們要獲得問題2的結果

若是隻是想要問題2的結果咱們就能夠進行優化了,用一下你們都很熟悉的四則運算
sub(f(x),x) = f(x) - x = x + x + 1 - x = x + 1 = add(x, 1)
也就是說
sub(f(x), x) 等價於 add(x, 1)
最終
g(x) = add(x, 1)

let n = 10;
let result2 = add(n, 1);複製代碼

好的,這就是咱們最終問題的答案了。

未完待續……


最後,團隊爲了招聘方便,整了個公衆號,主要是一些招聘信息,團隊信息,全部的技術文章在公衆號裏也能夠看到,對了,若是你想去美團其餘團隊,咱們也能夠幫你內推哦 ~

二維碼
二維碼
相關文章
相關標籤/搜索