JavaScript函數編程-Ramdajs


ramdajs函數式編程

在JavaScript語言世界,函數是第一等公民。JavaScript函數是繼承自Function的對象,函數能作另一個函數的參數或者返回值使用,這便形成了我們常說的高階函數(或稱函數對象)。這就構成函數編程的第一要素。在JavaScript世界中有很多的函數式編程庫能輔助我們的JavaScript函數式體驗,在它們之中最為成功的要數Underscore或lodash。

如下lodash實例代碼:

var users = [
  { 'user': 'barney',  'age': 36 },
  { 'user': 'fred',    'age': 40 },
  { 'user': 'pebbles', 'age': 18 }
];

var names = _.chain(users)
    .pluck('user')
    .join(" , ")
    .value();
console.log(names);

它以鏈式、惰性求值著稱,形成了一套自有的DSL風格。更多關於lodash的編程可以參見博主的另一篇文章JavaScript工具庫之Lodash

函數式思想展現的是一種純粹的數學思維。函數並不代表任何物質(對象,相對於面向對象思想而言),而它僅僅代表一種針對數據的轉換行為。一個函數可以是原子的算法子(函數),也可以是多個原子算法子組成的組合算法子。它們是對行為的最高抽象,具有非凡的抽象能力和表現力。

雖然Underscore或lodash也提供了.compose(或.flowRight)函數來實現函數組合的能力,但ramdajs具有更強的組合力。

ramdajs是一個更具有函數式代表的JavaScript庫,可以在這里了解更多關於它的信息http://ramdajs.com/0.17/。它的這種能力主要來自它自有的兩大能力:自動柯里化和函數參數優先於數據。

自動柯里化

在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受余下的參數且返回結果的新函數的技術。這個技術由 Christopher Strachey 以邏輯學家 Haskell Curry 命名的,盡管它是 Moses Schnfinkel 和 Gottlob Frege 發明的。

在理論計算機科學中,柯里化提供了在簡單的理論模型中比如只接受一個單一參數的lambda 演算中研究帶有多個參數的函數的方式。

ramdajs利用這一技術,默認所有API函數都支持自動柯里化。這為它提供了可以將另一個函數組合的先決條件。如常用的map操作需要接受兩個參數,在ramdajs中可以如下兩種方式實現:

R.map(function(item){
    return item *2;
 }, 
 [2,3,5]
); //輸出[4, 6, 10]


var map = R.map(function(item){
    return item *2;
});
map([2,3,5]); //輸出[4, 6, 10]

如果我們傳入2個完備的參數,則R.map函數將會直接執行。否則,它將返回另一個函數,等待參數完備時才執行。

關於JavaScript函數的柯里化,你還可以從博主的《JavaScript函數柯里化》中了解更多http://www.cnblogs.com/whitewolf/p/4495517.html

函數參數優先於數據

在UnderScore和lodash這類庫中,都要求首先傳入數據,然后才是轉換函數。而在ramdajs卻是顛覆性的改變。在它的規約中數據參數是最后一個參數,而轉換函數和配置參數則優於數據參數,排在前面。

將轉換函數放置在前面,再加上函數的自動柯里化,就可以在不觸及數據的情況下,將一個函數算法子包裝進另一個算法子中,實現兩個獨立轉換功能的組合。

假設,我們擁有如下兩個基礎算法子:

  1. R.multiply(a, b):實現 a *b; 2:R.map(func, data):實現集合 a –> b的map。

因為可以自動柯里化,所以有

R.multiply(10, 2); // 20

R.multiply(10) (2); // 20

所以上面對數組map的例子則可以轉為如下形式:

R.map(R.multiply(2)) ([2, 5, 10, 80]); // [4, 10, 20, 160]

R.map(R.multiply(2))的返回值也是一個函數,它是一個組合轉換函數。它組合了map和multiply行為。它利用R.map組合封裝了R.multiply(2)返回的柯里化函數,它等待map函數傳入對應的被乘數。

ramdajs的組合

有了上面的兩個條件,再加上ramdajs為我們提供的R.compose方法,我們就能很容易的實現更多算法子的組合。R.compose是從右向左執行的數據流向。

用ramdajs的組合來實現開篇lodash一樣的用戶名拼接的例子,則我們可以分為2個算法子的組合:

  1. R.pluck(prop):選擇對象固定屬性;
  2. R.join(data):對數組的字符串拼接。

則代碼如下所示:

var joinUserName = R.compose(R.join(" , "), R.pluck("user"));
joinUserName(users); // "barney , fred , pebbles"

這里的函數式組合可表示為下圖:

函數式組合

如果我們希望join用戶的年齡,則如下:

var joinUserAge = R.compose(R.join(" , "), R.pluck("age"));
joinUserAge(users); // "36 , 40 , 18"

假設我們希望輸出的不是用戶年齡,而是用戶生日,則我們可以輕易組合上一個減法的算法子:

  1. R.subtract(a, b):實現 a – b 數學算法。

則代碼如下:

var joinUserBrithDay = R.compose(R.join(","),R.map(R.subtract(new Date().getFullYear())),R.pluck("age"));
joinUserBrithDay(users); // "1979,1975,1997"

再如,我們希望獲取最年輕的用戶:

lodash實現:

_.chain(users)
  .sortBy("age")
  .first()
  .value();

ramdajs則,可以組合獲取第一個元素的R.head算法子和排序算法子R.sortBy:

var youngestUser = R.compose(R.head, R.sortBy(R.prop("age")));
youngestUser(users); // Object {user: "pebbles", age: 18}

比如我們希望獲取年長的用戶,則只需再組合一個反序排列的算法子R.reverse:

var olderUser = R.compose(R.head, R.reverse, R.sortBy(R.prop("age")));
olderUser(users); // Object {user: "fred", age: 40}         

希望你也能像我一樣喜歡上ramdajs,關於它的更多資料,請參見其官網 http://ramdajs.com/0.17/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM