Functional Programming 其实是我相对不熟的主题,但因为在写一些较难的程序时,往往会突然感受到有这些神奇的力量存在(?),再加上现在 forEach
、map
、filter
这麽普及,总觉得趁这个机会好好整理一下。
Functional Programming (简称 FP),是一种撰写风格,我觉得更像是一种抽象化的思维,因为用这种思维下去写的 code,会以 function 为操作的主体。
我觉得 FP 有点像是生产线的思维,第一个员工专门负责备料,第二个员工会接着用这些材料制作成料理,第三个员工就把这些料理送到客人手上。
而每一个员工都像是一个 function,它们不管其它的,只管自己手上的任务,它们的台词可能是像这样:
让大家对 FP 有个超级基础的概念,但今天还不会真的写到 code,因为我们要先来了解很多 FP 的名词跟观念:
要写 FP 的首要条件就是,function 必须是这个语言的一等公民,代表跟其他资料型别具有同等地位,也就是要拥有这些特性:
const addNum = (x, y) => x + y;
arr.map(num => num + 1);
const addNum = (x) => {
return (y) => {
return x + y;
};
};
这是硬条件哦!少了这个就不能叫 FP 了。
以下两者符合一项,即是高阶函式(简称 HoF):
看到这两句的当下会愣住,想一下会觉得:「蛤?函式里面还有另一个函式?我函式你的函式!听起来好强好高阶哦!」 - 高阶函式
对不起,这个词真的不是这样来的。
虽然我们一般写 function,会拿来当参数的,大部分都是 string、array 或 object 之类的。
但仔细一想却会发现:
arr.forEach(() => {});
arr.map(() => {});
arr.filter(() => {});
arr.reduce(() => {}, initialValue);
啊啊啊原来到处都在拿 function 当参数啊!虽然也没有很普遍,但起码不算陌生。不过。。。
比起 HoF 「是什麽」,网路上反而很少在讨论「为什麽」要有 HoF?它给 FP 带来了什麽好处?
其实我还真不知道,於是我也乖乖去 google 了一下:
functional programming higher order functions "why"
没错我还特地把 why 用双引号框起来,才比较找得到
得到一个最重要的结论是,HoF 让程序可以比较容易「抽象化」。
我试着讲讲我的理解,这边很期待能有朋友一起补充。
比如我们熟悉的 filter
就是 HoF,那如果在没有 HoF、没有 filter
的情况下,我们要怎麽做到「筛选」这件事呢:
// 筛选出 10 以下的数字
const arr = [3, 6, 9, 12, 15];
const lessThanTen = [];
for (let i=0; i<arr.length; i++) {
if (arr[i] < 10) {
lessThanTen.push(arr[i]);
}
}
console.log(lessThanTen);
执行结果
[3, 6, 9]
那现在使用 HoF 来做,会变成:
// 筛选出 10 以下的数字
const arr = [3, 6, 9, 12, 15];
const lessThanTen = arr.filter(num => num < 10);
console.log(lessThanTen);
执行结果
[3, 6, 9]
OK,先不要把重点放在 filter
的 code 比较少这件事,因为如果把 filter
底层的 code 翻出来,执行的量绝对不会少於上面的 for
回圈。
重点在於,我们抽象化了「筛选」这个动作。
我们抽象化了「筛选」这个动作。
抽象化了「筛选」。
系统提示:你看到脑中的回音了
抽象化并不是把 for
回圈拉出去当 function 那麽简单:
// 筛选出 10 以下的数字
const lessThanTenFilter = (inputArr) => {
const lessThanTen = [];
for (let i=0; i<inputArr.length; i++) {
if (inputArr[i] < 10) {
lessThanTen.push(inputArr[i]);
}
}
return lessThanTen;
};
const arr = [3, 6, 9, 12, 15];
const result = lessThanTenFilter(arr);
console.log(result);
如果我今天需要筛选的是
我是不是还要为了这几个特别的 case,又多写三个 function 出来?
听起来就很难维护啊!所以我想抽象化就是为了解决这个问题,可能称作「客制化」的问题吧!
我们把筛选这个动作抽象化,所有想要做「筛选」动作的,都可以呼叫 filter
,然後根据你的需求,把判断用的 function 丢进参数,就完成一个「客制」的 filter
了。
const arr = [3, 6, 9, 12, 15];
// 筛选出 5 以下的数字
arr.filter(num => num < 5);
// 乘以 3 是 2 的倍数的数字
arr.filter(num => (num * 3) % 2 === 0);
// 与今天月份相同的数字
arr.filter(num => num === new Date().getMonth() + 1);
执行结果
[3]
[6, 12]
[9]
所以或许可以这样说,HoF 能够赋予 function 在某个基础上客制化的能力。
比如我们自己来写一个,回传 function 的 HoF:
const addNum = (x) => {
return (y) => {
return x + y;
};
};
// 可简化成
// const addNum = x => y => x + y;
const addFive = addNum(5);
const addTen = addNum(10);
addFive(3); // 8
addTen(3); // 13
有感受到了吗?透过 addNum
这个 HoF,我们可以很快「客制」出两个额外的 function,分别处理 +5 与 +10 的 case,这是抽象化非常厉害的地方呢!
如果我说明得不够清楚,也欢迎大家补充,或者可以看看 Quora 的网友怎麽看
关於纯函式的定义,在维基可以看到比较精准的定义:
函式与外界交换资料只有一个唯一渠道——参数和回传值
- 函式从函式外部接受的所有输入资讯,都通过参数传递到该函式内部
- 函式输出到函式外部的所有资讯,都通过回传值传递到该函式外部
白话一点:
在函式内出现的变数,要嘛是函数内自己宣告的,要嘛是从参数传进来的,有其他来源的话就是 impure
而 impure 的函式,就代表函式里面有 side effects。
side effects 我们有在 Day 6 - Function 时空旅行 (1) 提到过,如字面上的意思就是副作用,翻成白话应该是:「你做的事影响到其它人」。
常见的 side effects 如下:
fetch
、axios
)console.log
)docuement.querySelector
)我想许多人会感到困惑的应该是这个吧。。。
console.log
怎麽也算 side effects 啊!它招谁惹谁了QQ 把东西印出来又不会出事!
这部分算是我也还在理解的,我想是因为 pure function 要的是完全的纯粹,也就是这个 function 里面只要做好它「该做的事」。
而像 console.log
这样其实是去呼叫 window.console.log
的指令,一来它「不是该做的事」,二来它就是「影响到别人了」。
这边需要强调一点,不用强硬追求 100% 的 pure,或者 100% 没有 side effects,因为如果真的达到 100% 了,是不是也不能够发送 http request 跟操作 DOM 了呢?
我认为要追求的是,尽可能让有 side effects 的程序码被集中(共用),不要东一个西一个,才能够将测试时的负担降到最低。
Pure 追求的不是
zero side effects
而是
minimize side effects
今天介绍了关於 FP 几个常见的特性,尤其是关於 HoF 的意义,我自己也在查资料的过程中思考了许多,有一些思维其实没有一定的做法,但总是会在碰到某些困难时,灵光一闪觉得「好像可以这样用!」,我想这就是学习不同 coding 思维很有趣的地方!
幻化
在空旷的荒野洒落
通向八方的道路
hannahpun - Function Programming In JS
Po-Ching Liu - javascript-functional-programming
Why-are-higher-order-functions-important-to-functional-programming
<<: [Day 12] 第一主餐 pt.5-MySQL Django一起串联,就是这麽简单
>>: D8 - 彭彭的课程#Python 集合、字典的基本运算 - Set、Dictionary
LINE JP 电脑版 个别视窗 聊天室 变成 半透明 如何改回来 拉动上面那个 就回来了 #3小姐...
from selenium import webdriver import openpyxl imp...
前几天我们把购物车流程跑完了,其中有讲到优惠的部份, 因此我们订单需要新增table来储存该内容,这...
slate 将 typescript 的型别扩充相关的内容都集合在 interfaces/cust...
今日目标 轻松小品,来点 OpenCV 人脸辨识的实作 What is OpenCV? OpenCV...