從函數語言程式設計到Ramda函式庫(一)

Vadim發表於2019-04-10

  函數語言程式設計是種程式設計方式,它將電腦運算視為函式的計算。函式程式語言最重要的基礎是λ演算(lambda calculus),而且λ演算的函式可以接受函式當作輸入(引數)和輸出(返回值)。和指令式程式設計相比,函數語言程式設計強調函式的計算比指令的執行重要。和過程化程式設計相比,函數語言程式設計裡函式的計算可隨時呼叫。

  最近一直在研究函數語言程式設計,從函數語言程式設計中彷彿看到了js規範化的影子。大家都知道js是一門很靈活的程式語言,而這種靈活性在程式碼量的累積下會產生質量和可控性的問題。初學js的朋友大多都是結果導向,寫的程式碼剛剛夠用現有的專案,很難修改與擴充套件程式碼,而且不規範的程式碼常常讓隊友甚至是自己頭疼。而函數語言程式設計卻能改變這樣的現狀。

  再說函數語言程式設計之前按照慣例要說一些相關的知識。

1.什麼是純函式?

  純函式顧名思義就是很純的函式,大家都喜歡純粹的事物,那什麼樣的函式才配稱作純函式呢?
  簡單地說,純函式就是傳入相同的引數可以返回相同的值的函式,也就是說返回的結果只依賴於傳遞的引數,而和環境沒有關係。類似於我們數學中的函式的概念。

Array.prototype.map()
Array.prototype.slice()
String.prototype.toUpperCase()

  像上面這樣的函式就是純函式

Array.prototype.sort()
Math.random()

  像上面這樣的函式就不是純函式了
  純函式有很多優秀的性質讓我們喜歡,

1.純潔性

  她既不會被環境改變也不會改變環境,結果只依賴傳入值。那麼這就方便我們對函式進行快取。下面以求x的平方為例(這裡用到了ramda函式庫,沒接觸也沒關係,這裡只是講概念,之後我們會提及這個函式庫)

let square = R.memoizeWith(R.identity, x => x * x);
square(3); // => 9 第一次會呼叫原始方法並將引數和結果以key-value的形式儲存。
square(3); // => 9 這裡是呼叫快取
square(3); // => 9 這裡也是呼叫快取

2.易測試性

  由於純函式執行不需要環境,所以進行測試時我們只需要面對函式本身測試就可以了。這讓函式測試變得非常簡單

3.透明性

  如果一段程式碼可以替換成它執行所得的結果,而且是在不改變整個程式行為的前提下替換的,那麼我們就說這段程式碼是引用透明的。由於純函式總是能夠根據相同的輸入返回相同的輸出,所以它們就能夠保證總是返回同一個結果,這也就保證了引用透明性。

2.什麼是柯里化?

  函式柯里化(curry)的定義很簡單:傳遞給函式一部分引數來呼叫它,讓它返回一個函式去處理剩下的引數。下面我們對比一下柯里化之前的函式和柯里化的函式。

let add = (x, y, z) => x + y + z;
add(1,2,3) // => 6;
let add = (x, y, z) => {
  return z => {
    return y => {
      return x + y + z;
    }
  }
}
add(1)(2)(3) // => 6;

  後者就是柯里化的函式,它利用了函式的記憶性記住了每次傳你進去的引數並返回了可繼續執行的函式。是不是感覺發現了新大陸?

3.什麼是函式組合

  函式組合(compose)就是把多個純函式組合起來解決函式巢狀問題。下面我們來看兩個例子

let compose = (f, g, h) => x => f(g(h(x)));
let add = x => x + 4;
let mul = x => x * 2;
let mis = x => x - 1;
compose(add, mul, mis)(1) // => 3
let compose = (f, g, h) => x => f(g(h(x)));
let getFirst = arr => arr[0];
let getUpperCase = str => str.toUpperCase();
let getReverse = arr => arr.reverse();
compose(getUpperCase, getFirst, getReverse)(['v', 'a', 'd', 'i', 'm']); // => 'M'

  因為純函式的純潔性,我們可以把多個函式組合起來,注意一定是純函式。

4.什麼是point-free

point-free簡單地說就是無需使用所要處理的值,只關注運算過程。可以用公式 fn = R.pipe(f1, f2, f3);表示,也就是說如果事先定義了函式f1,f2,f3就可以推算出函式fn,因此無需使用引數形式,下面我們來對比一下兩種形式。

let noPointFree = word => word.toUpperCase().split('-');
let pointFree = compose(split('-'),toUpperCase);

  pointfree 模式能夠幫助我們減少不必要(中間變數)的命名,讓程式碼保持簡潔和通用。

  關於函數語言程式設計先講一下基礎的知識,下一節我來講一下js函數語言程式設計優秀的函式庫----Ramda函式庫。

 

原創部落格:轉載請註明從函數語言程式設計到Ramda函式庫(一)

相關文章