iT邦幫忙

2022 iThome 鐵人賽

DAY 24
1
Modern Web

致 JavaScript 開發者的 Functional Programming 新手指南系列 第 24

Day 24:高階函數與複合函數(2):科里化陣列方法

  • 分享至 

  • xImage
  •  

陣列方法可以說是 JavaScript 中最常使用的計算工具,不論是資料的處理,或是畫面的呈現,都少不了陣列方法。

在了解高階函式與複合函式的概念後,我們來試著讓陣列方法的復用性更高吧!

在前面的章節中,我們曾經使用過 map 進行資料的處理:

const list = [
	{
		name: '小明',
		grade: 5,
	},
	{
		name: '小可',
		grade: 1,
	},
	{
		name: '小櫻',
		grade: 3,
	},
];
const newList = list.map((item) => ({...item, grade: item.grade+1}));

但如果我們要針對這個陣列進行重複性的處理的話,可能會變成:

const doubleGrade = list.map((item) => ({...item, grade: item.grade * 2})); 
const tripleGrade = list.map((item) => ({...item, grade: item.grade * 3})); 

如果今天要處理的資料一多,多個不同的陣列同時進行類似的運算時,我們的程式碼可能就會長成:

const doubleGradeList1 = list1.map((item) => ({...item, grade: item.grade * 2})); 
const tripleGradeList1 = list1.map((item) => ({...item, grade: item.grade * 3})); 
const doubleGradeList2 = list2.map((item) => ({...item, grade: item.grade * 2})); 
const tripleGradeList2 = list2.map((item) => ({...item, grade: item.grade * 3})); 

一旦我們的程式架構複雜龐大起來,我們的軟體就會被一堆重複且不必要的程式碼,此時透過抽象化,將一些重複的細節拆解出來的話,就可以讓程式碼的復用性更高:

const doubleItemGrade = item => ({...item, grade: item.grade * 2});
const tripleItemGrade = item => ({...item, grade: item.grade * 3});
const doubleGradeList1 = list1.map(doubleItemGrade(i)); 
const tripleGradeList1 = list1.map(tripleItemGrade(i)); 
const doubleGradeList2 = list2.map(doubleItemGrade(i)); 
const tripleGradeList2 = list2.map(tripleItemGrade(i));

在上方的範例中,我們應用了高階函式的技巧,在 map 中分別傳入 doubleItemGradetripleItemGrade 函式,未來若是要處理類似資料結構的屬性計算,就不需要一一在每個 map 函式中撰寫一次新的函式。

當然,如果我們認真觀察後,可能還會發現一些重複的細節,例如:被不同手段處理的陣列本人可以被當成固定參數,或是我們可以把處理 grade 屬性的方法的倍數由外層決定!於是我們就可以再次利用科里化的方式,嘗試固定住陣列的資料,所以程式碼就會變成:

const multipleGrade = times => item => ({...item, grade: item.grade * times});
const curriedMap = ary => func => ary.map(func);
const dealWithList = curriedMap(list);
const doubleGrade = dealWithList(multipleGrade(2));
const tripleGrade = dealWithList(multipleGrade(3));
...

登愣!當程式碼要處理的邏輯一多時,我們就透過抽象化思考,將重複的邏輯封裝成一個函式,這些函式的細節全由參數做決定,同時夠過複合函式與高階函式的重複組合,讓大部分的程式碼可以重複再利用。

當然,除了 map 以外,JavaScript 中的陣列方法,都可以透過柯里化的技巧,讓程式碼看起來一致也更「FP」:

const curriedMap = ary => func => ary.map(func);
const curriedFilter = ary => func => ary.filter(func);
const curriedRuduce = init => funcs => funcs.reduce(((x, func) => func(x) ), init);

看到這裡,也許你會跟我當初初識 FP 時一樣驚訝,除了 mapfilter 外,連 reduce 都可以透過複合函式與高階函式的手段進行科理化。

透過這個技巧優化完的函式,完完全全符合我們先前在聊到抽象化時,所提到的「廣義化」與「特殊化」,這些函式不僅可以符合大部分計算的需求,也可以透過柯里化固定參數的手法,讓函式可以針對一些特定的計算,讓資料再次重複使用。

而比起 mapfilter ,我認為 reduce 能處理的任務又更複雜了一點, 如果這邊覺得 curriedRuduce 有點複雜也沒關係,在下一章節中,我們會針對 curriedRuduce 進行拆解,並透過柯里化 reduce 來實作能讓前端函式自動化的 pipe !那我們就下一章節見啦。


上一篇
Day 23 :高階函數與複合函數(1):進階的函數應用
下一篇
Day 25:高階函數與複合函數(3):reduce 實用方法
系列文
致 JavaScript 開發者的 Functional Programming 新手指南30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言