js函數語言程式設計(二)-柯里化

Yang.kid發表於2018-09-13
這節開始講的例子都使用簡單的TS來寫,儘量做到和es6差別不大,正文如下

我們在程式設計中必然需要用到一些變數儲存資料,供今後其他地方呼叫。而函數語言程式設計有一個要領就是最好不要依賴外部變數(當然允許通過引數傳遞咯),如何解決這個矛盾的問題呢?將函式柯里化`Curry`就可以了,這種技巧可以讓函式記住一些歷史資料,也就是快取,怎麼做呢?

說柯里化之前必須先說一下閉包,因為這是實現柯里化的方法。

閉包

const fun = () => {
  let a = 0;
  return () => {
    a += 1;
    return a;
  };
};
const fa = fun();
console.log(fa()); // 1
console.log(fa()); // 2
console.log(fa()); // 3

每次執行這個函式居然結果不一樣,why?因為`a`是`fun`函式內部變數,而這個變數又被`fun`返回的函式依賴,`fun()`執行後,fa所指向的那個函式(`fun`的返回值)還在依賴著`a`。根據js的垃圾回收機制,只要`fa`存在,那麼`a`就不會被釋放,一直在記憶體中。

進一步想,既然`a`作為區域性變數被依賴著就一直存在,那麼這個區域性變數要是引數是不是也有同樣的效果呢,答案是肯定的。

const fun = (a: number) => {
  return () => {
    a += 1;
    return a;
  };
};
const fa = fun(1);
console.log(fa()); // 2
console.log(fa()); // 3
console.log(fa()); // 4

好,閉包就講這麼多。回到柯里化。

柯里化

柯里化由函式閉包實現。我們把上面的例子改一改

const fun = (a: number) => (b: number) => {
  return a + b
};
const add2 = fun(2);
console.log(add2(2)); // 4
console.log(add2(3)); // 5
console.log(add2(4)); // 6

`fun(2)`就是`(b) => b+2`,所以`add2(2)`自然是`4`。以上就是柯里化,引數可以先後傳入,全部傳完才計算。以上是es6或ts的程式碼實現,比較簡陋。事實上你完全不必要自己去實現一個函式的柯里化。可以使用成熟的庫如ramda。ramda庫還包含了很多已經柯里化的函式。

import { curry } from `ramda`
const fun = curry((a: number, b: number) => {
  return a + b
});
const add2 = fun(2);
console.log(add2(2)); // 4
console.log(add2(3)); // 5
console.log(add2(4)); // 6

把一個你要柯里化的函式,原封不動的給curry函式,返回的就是柯里化的函式。但要注意函式引數從左往右的順序是依次傳給柯里化後函式的順序。have fun!

相關文章