關於函數語言程式設計的思考(2)

前端深夜告解室發表於2017-08-25

作者:李英傑,美團金融前端團隊成員。歡迎大家一起來探討FP

題外話:只是單純地談談個人對函數語言程式設計的理解,歡迎大家來一起探討。也不會提及高階函式與範疇學的內容,只聊一些很入門的問題。函數語言程式設計的優點這裡也不做過多說明,會推薦大家看幾篇文章,裡面有很好的闡述。

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

目錄

  1. 為什麼函數語言程式設計在前端復興?

  2. 什麼是函數語言程式設計?

  3. 函數語言程式設計如何思考問題?

  4. 函數語言程式設計與物件導向程式設計有什麼區別,各自的優缺點是什麼?

  5. map,reduce是函數語言程式設計嗎?

  6. 推薦


書接上文

4.用這道很簡單的應用題,我們來嘗試對比一下函數語言程式設計與物件導向程式設計的區別。

其實這個應用題給了一些暗示,讓大家不知不覺走到了“陷阱”中

  • 標明這是應用題,引導大家用數學的思維去思考
  • 給出的已知條件,進一步引導大家用四則運算去思考

把這兩個暗示去掉,我們再來看一下這個問題
小雨帶著n個蘋果出去玩,路過薛大叔家,薛大叔給了她n個蘋果,薛大嬸給了她1個蘋果。
路過張磚頭家,小雨給了張磚頭n個蘋果
小雨現在有多少個蘋果?

用物件導向的思維該如何解決這個問題?
第一步 抽象個類
第二步 新增屬性和方法

class Person {
  constructor(name, apples) {
    this.name = name;
    this.apples = apples;
  }

  receive(num) {
    this.apples += num;
  }

  give(num) {
    this.apples -= num;
  }

  getApples() {
    console.log(this.apples);
  }
}

let n = 10;
let xiaoYu = new Person('小雨', n);
xiaoYu.receive(n);// 薛大叔給了n個
xiaoYu.receive(1);// 薛大嬸給了n個
xiaoYu.give(n);// 給了張磚頭n個
let result = xiaoYu.getApples();// 答案複製程式碼

對比一下剛剛函式式思維的程式碼

let n = 10;
// 加法
function add(x, y) {
  return x + y;
}
// 減法
function sub(x, y) {
  return x - y;
}
let result = add(n, 1);// 答案複製程式碼

首先說說函式式思維程式碼的優點

  • 程式碼更加簡潔,行數更少
  • 執行更加高效
  • 測試更加簡單。如果我們要測試送蘋果這個動作,只需要測試sub這個函式。但是對於物件導向思維的程式碼,我們需要new一個物件,初始化name與apples,執行give方法,驗證getApples得到的結果是否正確。
    看一個吐槽:“物件導向程式語言的問題在於,它總是附帶著所有它需要的隱含環境。你想要一個香蕉,但得到的卻是一個大猩猩拿著香蕉,而其還有整個叢林。” — Joe Armstrong(Erlang語言發明人)

再說一說函式式思維程式碼的缺點

  • 直接給了結果add(n, 1),如果不知道你的推導過程,根本不知道你究竟在搞什麼。
  • 如果不寫出你的思路,完全不知道這個題目跟add與sub函式有什麼關係

從以上分析的優缺點,我們可以初步理解為什麼函數語言程式設計會很高效。
但是同時也要清楚,函數語言程式設計要比物件導向程式設計更加需要註釋,要清楚地描述是如何將實際問題抽象成數學問題的,推導過程是怎樣的。
通過對比也可以看出來,物件導向的程式碼相對容易理解,而容易理解是非常重要的優勢,因為我們常說:要寫出人能理解的程式碼。

如果上面的應用題的一個條件改為
“路過張磚頭家,小雨給了張磚頭5個蘋果”
兩種思維方式的改動成本也是不一樣的。對於物件導向思維,需要找到描述這個動作的程式碼,進行修改;對於函式式思維可能需要重新推導相關流程。

物件導向的思維在努力地告訴我們:說出你的世界,我們用程式碼描述它
函式式的思維在冷冰冰地跟我們說:說出你的世界,我們把它轉換成數學

這個例子應該會引起很多人的疑惑,這是一道應用題,簡單用了個加減乘除,跟函數語言程式設計沒有什麼關係啊。
    其實我自己也很苦惱啊,該怎麼把這個事情圓回來啊

如果大家認可這道題用加減法去做才是最正常的思維,那恭喜你已經有了函式式的思維,大家已經很自然地把題目中小明收到蘋果抽象成了加法,小明給出去蘋果抽象成了減法。你們已經做到了第一步——把實際問題抽象為數學問題 :)
    再吹一波我朝的基礎教育。這是一個應付98元,給收銀員103元,秒找5元的國度。
四則運算儘管沒有那麼高大上,但是還真可以跟函數語言程式設計扯上點兒關係。運算子號+和運算子號-是可以當成函式看待的,運算子號的兩邊的數字看做函式輸入,結果看做函式輸出。如果感興趣,可以看一下Haskell中對四則運算子的使用,相信看過之後大家會有更加直觀的感受。

5.最後,map、reduce是不是函數語言程式設計?

這是一個好問題,也是一個很有意思的問題,確實有很多函數語言程式設計的文章在講解並且強調這些函式的用法。
我想用另一個問題來解釋一下這個問題:類是不是物件導向程式設計?
很多人會告訴我,類是物件導向程式設計中一個很重要的概念,但是不應該僅僅用有沒有類來區分是不是物件導向的思維,即使用了類,如果不理解物件導向的思維,依舊會寫出很多不是物件導向的程式碼。
我覺得同樣可以解釋一下map和reduce:這是函數語言程式設計中比較常用的函式,但是如果你不理解函數語言程式設計的思維,依舊可以用map和reduce寫出很多非函式式的程式碼。

6.推薦

裡面有一本講haskell的書,這種為函式式而生的語言,個人覺得大家可以瞭解一下。簡單看看Haskell語言的設計,會讓大家有一些收穫。
    看到Haskell的函式天生柯里化,可能會讓你感到這個語言的神奇,同時也可以思考一下js的柯里化
    看到 (+3) 可以作為一個函式使用的時候,可能會讓你感到這個世界的神奇

看看名人大家是如何吐槽物件導向的

傻瓜函數語言程式設計

JS函數語言程式設計指南

HASKELL 趣學指南

Functor, Applicative, 以及 Monad 的圖片闡釋


最後,團隊為了招聘方便,整了個公眾號,主要是一些招聘資訊,團隊資訊,所有的技術文章在公眾號裡也可以看到,對了,如果你想去美團其他團隊,我們也可以幫你內推哦 ~

二維碼
二維碼

相關文章