JavaScript函數語言程式設計之pointfree與宣告式程式設計
更多相關內容見部落格 github.com/zhuanyongxi…
函數語言程式設計中的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,
)
);
};
參考文章
原文釋出時間為:2018年06月17日
原文作者:磚用冰西瓜
本文來源: 掘金 如需轉載請聯絡原作者
相關文章
- 瞭解 JavaScript 函數語言程式設計 - 宣告式函式JavaScript函數程式設計函式
- javascript函數語言程式設計JavaScript函數程式設計
- JavaScript 函數語言程式設計JavaScript函數程式設計
- Scala函式與函數語言程式設計函式函數程式設計
- JavaScript函數語言程式設計之副作用JavaScript函數程式設計
- JavaScript函數語言程式設計,真香之組合函式(二)JavaScript函數程式設計函式
- JavaScript函數語言程式設計之深入理解純函式JavaScript函數程式設計函式
- 函數語言程式設計-鏈式程式設計RAC函數程式設計
- JavaScript 函數語言程式設計(二)JavaScript函數程式設計
- JavaScript 函數語言程式設計(一)JavaScript函數程式設計
- JavaScript 函數語言程式設計(三)JavaScript函數程式設計
- JavaScript函數語言程式設計(二)JavaScript函數程式設計
- JavaScript函數語言程式設計(一)JavaScript函數程式設計
- JavaScript函數語言程式設計(三)JavaScript函數程式設計
- iOS鏈式程式設計及函數語言程式設計iOS程式設計函數
- javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計JavaScript函數程式設計函式
- JavaScript中的函數語言程式設計JavaScript函數程式設計
- [譯] JavaScript 函數語言程式設計指引JavaScript函數程式設計
- 『翻譯』JavaScript 函數語言程式設計JavaScript函數程式設計
- JavaScript函數語言程式設計學習JavaScript函數程式設計
- JavaScript 函數語言程式設計介紹JavaScript函數程式設計
- JavaScript 中的函數語言程式設計JavaScript函數程式設計
- JavaScript 函數語言程式設計導論JavaScript函數程式設計
- iOS 與 函數語言程式設計iOS函數程式設計
- 深入理解javascript系列(十二):函式與函數語言程式設計(1)JavaScript函式函數程式設計
- 深入理解javascript系列(十三):函式與函數語言程式設計(2)JavaScript函式函數程式設計
- 函數語言程式設計函數程式設計
- 函數語言程式設計:Lambda 表示式函數程式設計
- Scala 函數語言程式設計(一) 什麼是函數語言程式設計?函數程式設計
- Kotlin 函式與函數語言程式設計淺析Kotlin函式函數程式設計
- 函數語言程式設計之Compose函數程式設計
- Python之函數語言程式設計Python函數程式設計
- JavaScript 函數語言程式設計---柯里化JavaScript函數程式設計
- 深入學習javascript函數語言程式設計JavaScript函數程式設計
- 我眼中的 JavaScript 函數語言程式設計JavaScript函數程式設計
- JavaScript學習(3):函數語言程式設計JavaScript函數程式設計
- 好程式設計師Python培訓分享函數語言程式設計之匿名函式程式設計師Python函數函式
- 函式式思維和函數語言程式設計函式函數程式設計