JavaScript函數式編程之pointfree與聲明式編程

函數式編程中的pointfree的意義就是“無參”或“無值”,pointfree style是一種編程範式,也作tacit programming,就是“無參編程”的意義了。什麼是“無參編程”?

// 這就是有參的,由於有word
var snakeCase = word => word.toLowerCase().replace(/\s+/ig, '_');

// 這是pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);

從另一個角度看,有參的函數的目標是獲得一個數據,而pointfree的函數的目標是獲得另一個函數。
所以,以下的方程,雖然也有參,也能夠認為是pointfree的。

const titlesForYear = year =>
  pipe(
    filter(publishedInYear(year)),
    map(book => book.title)
  )

那這pointfree有什麼用?
它能夠讓我們把注重力集合在函數上,參數定名的貧苦肯定是省了,代碼也更簡約文雅。
須要注重的是,一個pointfree的函數多是由浩瀚非pointfree的函數構成的,也就是說底層的基本函數多數是有參的,pointfree表現在用基本函數組合而成的高等函數上。假如我們運用函數式編程的東西,如ramda,這些基本函數多數已被寫好了,如許我們去寫pointfree的代碼就很輕易了。

什麼是聲明式編程?它區分於敕令式編程

// 敕令式
var words = [];
for (i = 0; i < otherWords.length; i++) {
  words.push(otherWords[i].word);
}

// 聲明式
var words = otherWords.map(function(ele){ return ele.word; });

輕易看出,敕令式的代碼,我們不只要去遍歷,還要關注怎樣遍歷。而聲明式的就輕易許多,能夠節約我們的注重力,代碼也越發簡約。

其他的敕令式的寫法有:運用ifelse舉行的前提推斷,運用算數運算符舉行的算數運算,運用比較運算符舉行的比較運算和運用邏輯運算符舉行的邏輯運算。

至於那些說“雖然云云,但運用敕令式輪迴速率要快許多”的人,我發起你們先去學學 JIT 優化代碼的相干學問。這裡有一個
異常棒的視頻,能夠會對你有協助。

須要注重的是,要完成這類聲明式的編程,起首我們要有這個map要領,這一點與pointfree雷同,都是須要我們先對經常使用的操縱做一次封裝,而這些經常使用的操縱自身照樣敕令式的。

pointfree的聲明式代碼是函數式編程應當有的模樣。

最後用一個來自Scott Sauyet的文章《Favoring Curry》中的例子,運用的函數式東西是ramda。下面的代碼不須要一句一句的看,也許體味一下就能夠了。

一組JSON數據

var data = {
    result: "SUCCESS",
    interfaceVersion: "1.0.3",
    requested: "10/17/2013 15:31:20",
    lastUpdated: "10/16/2013 10:52:39",
    tasks: [
        {id: 104, complete: false,            priority: "high",
                  dueDate: "2013-11-29",      username: "Scott",
                  title: "Do something",      created: "9/22/2013"},
        {id: 105, complete: false,            priority: "medium",
                  dueDate: "2013-11-22",      username: "Lena",
                  title: "Do something else", created: "9/22/2013"},
        {id: 107, complete: true,             priority: "high",
                  dueDate: "2013-11-22",      username: "Mike",
                  title: "Fix the foo",       created: "9/22/2013"},
        {id: 108, complete: false,            priority: "low",
                  dueDate: "2013-11-15",      username: "Punam",
                  title: "Adjust the bar",    created: "9/25/2013"},
        {id: 110, complete: false,            priority: "medium",
                  dueDate: "2013-11-15",      username: "Scott",
                  title: "Rename everything", created: "10/2/2013"},
        {id: 112, complete: true,             priority: "high",
                  dueDate: "2013-11-27",      username: "Lena",
                  title: "Alter all quuxes",  created: "10/5/2013"}
        // , ...
    ]
};

需求是找到Scott一切未完成的使命,並根據到期日期升序分列。

準確的結果是

[
    {id: 110, title: "Rename everything", 
        dueDate: "2013-11-15", priority: "medium"},
    {id: 104, title: "Do something", 
        dueDate: "2013-11-29", priority: "high"}
]

敕令式的代碼以下

getIncompleteTaskSummaries = function(membername) {
    return fetchData()
        .then(function(data) {
            return data.tasks;
        })
        .then(function(tasks) {
            var results = [];
            for (var i = 0, len = tasks.length; i < len; i++) {
                if (tasks[i].username == membername) {
                    results.push(tasks[i]);
                }
            }
            return results;
        })
        .then(function(tasks) {
            var results = [];
            for (var i = 0, len = tasks.length; i < len; i++) {
                if (!tasks[i].complete) {
                    results.push(tasks[i]);
                }
            }
            return results;
        })
        .then(function(tasks) {
            var results = [], task;
            for (var i = 0, len = tasks.length; i < len; i++) {
                task = tasks[i];
                results.push({
                    id: task.id,
                    dueDate: task.dueDate,
                    title: task.title,
                    priority: task.priority
                })
            }
            return results;
        })
        .then(function(tasks) {
            tasks.sort(function(first, second) {
                var a = first.dueDate, b = second.dueDate;
                return a < b ? -1 : a > b ? 1 : 0;
            });
            return tasks;
        });
};

pointfree的代碼

var getIncompleteTaskSummaries = function(membername) {
  return fetchData()
    .then(R.prop('tasks'))
    .then(R.filter(R.propEq('username', membername)))
    .then(R.reject(R.propEq('complete', true)))
    .then(R.map(R.pick(['id', 'dueDate', 'title', 'priority'])))
    .then(R.sortBy(R.prop('dueDate')));
};

pointfree的聲明式的代碼

// 提取 tasks 屬性
var SelectTasks = R.prop('tasks');

// 過濾出指定的用戶
var filterMember = member => R.filter(
  R.propEq('username', member)
);

// 消除已完成的使命
var excludeCompletedTasks = R.reject(R.propEq('complete', true));

// 拔取指定屬性
var selectFields = R.map(
  R.pick(['id', 'dueDate', 'title', 'priority'])
);

// 根據到期日期排序
var sortByDueDate = R.sortBy(R.prop('dueDate'));

// 合成函數
var getIncompleteTaskSummaries = function(membername) {
  return fetchData().then(
    R.pipe(
      SelectTasks,
      filterMember(membername),
      excludeCompletedTasks,
      selectFields,
      sortByDueDate,
    )
  );
};

參考文章

我在github https://github.com/zhuanyongx…

    原文作者:磚用西瓜
    原文地址: https://segmentfault.com/a/1190000014591764
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞