「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」
之前分享过一篇关于函数式编程感觉还不到位,有许多函数编程中的知识点还有没有涉及到,还有就是学习函数式编程是一种思想,我们不是模仿或者生搬硬套,而需要我们通过学习和思考将这个思想或者说方法论融入我们的 code 中,成为有助于我们解决自己身边实际问题。
什么是函数式编程
- 并不是新生事物,早在 50-60s 就有人提出函数式编程
- 不是一门语言,当然也不是一门语言,只是一种编程风格
- 不是命令式也不是面向对象编程,对于 javascript 也可以看成一门支持函数式编程语言
- 不是解决所有问题的完美解决方案
- 不算难
函数式编程有哪些特点
- 不变性
- 高阶函数,也就是可以将函数作为变量和参数传递给函数,也可以将函数作为另一个函数返回值来使用
- 函数中不包含状态,也就是函数内部不会状态,因为不同的状态导致相同输入得到不同输出
- 引用透明性,也就是函数输出确定性,有了确定性也就是说明这个函数便于测试
- 递归
- 模式识别
- 无状态
- Mondas
在哪些语言对函数式编程是有好的
- 函数式编程语言(Haskell, Erlang, Elm, 等)
- .NET - c# F# 等
- Javascript
什么情况下适合函数式编程
- 数据处理
- 并行系统
- serverless
什么场景下不适合采用函数式编程
- UI
- 外部交互
- IO
- 高性能的系统
为什么是函数式编程
- 简介
- 可读性
- 易于测试
- 支持并行
- 鲁棒性
- 有趣
currying (柯里化)
Currying 是函数式编程中的一个过程,将有多个参数的函数转化为一系列嵌套的仅接受一个参数的函数,每返回一个函数,这个函数接受参数就是下一个参数。
例如 currying, 这个单词翻译过来柯里化,根据发音直译的单词。查字典有咖喱的意思,感觉不贴边。还有字典给出了加脂操作意思,看这个还比较贴边,最后找到currying 还有对食品进行腌制的意思,这个感觉就更贴近了 currying 含义
返回函数会持有执行该函数时所接受参数,然后就像剥洋葱似的,知道所有参数都使用到了,在没有传入最后一个参数前,其他参数都"live"接受这个参数的函数所形成的闭包中,当返回最后一个函数并执行时,所有参数就都会被使用,所以说 Currying 是函数式编程中一个过程。对于一些抽象概念用简介语言来解释清楚,的确需要有一定语言功底,这里就不多说了,我们直接上代码。估计通过 code 实例,大家就更好理解了。
function fn(a,b){}
function multiply(a,b,c){
return a * b * c;
}
multiply(1,2,3);
上面的函数 multiply 接受 3 个参数,将这 3 个参数相乘后得到结果作为函数返回值。接下解释如何用 currying 函数将参数分配到一系列调整函数,这些函数每一个都只接受一个参数。
function curried_multiply(a){
return (b) => {
return (c) =>{
return a * b * c;
}
}
}
curried_multiply(1)(2)(3);
现在 multiply(1,2,3)函数调用变成了multiply(1)(2)(3) 多个函数调用。也就是将一个的函数已经变成了一系列的函数。为了得到 1、2、3 三个数字的乘法结果,参数值一个接一个地传递,每个参数值都可以被随后函数所使用。
const mul1 = curried_multiply(1);
const mul2 = mul1(2);
const result = mul2(3);
console.log(result); // 6
现在开始详细地解释一下,如果觉得无聊可以跳过这部分内容,给 curried_multiply 函数传递参数为 1
return (b) => {
return (c) => {
return a * b * c
}
}
偏函数与 currying
function add(a){
return (b)=> a + b
}
const add2 = add(2)
res = add2(3);
console.log(res)
const githubFavicon = buildUri('https', 'github.com', 'favicon.ico')
const githubFavicon = buildHttpsUri('github.com', 'favicon.ico')
因为大多数情况下,网站都是 https 开头,那么对于 buildUri 函数每次都输入 ·https 这样看起来进行一些重复性的劳动。不过如果硬编码如 buildHttpsUri 开起来也不那么优雅。这时候我们可以使用偏函数来解决这个实际问题。
function buildUri(baseUrl){
return function(website,path){
//
}
}
currying 的应用
随着Redux JavaScript 库、Reason 语法扩展和工具链以及 Cycle JavaScript 框架这些新兴框架无疑都用到函数式编程,所以在 JavaScript 这门语言中,函数式编程正变得越来越重要。有两个源于函数式思想的重要思想是 currying。接下里看一看在实际如何使用 currying。
编写小的代码模块,可以轻松地重复使用和配置
可以用函数来保存状态或者策略,例如销售策略,对多种商品使用相同销售策略
function discount(price, discount) {
return price * discount
}
const price = discount(500,0.10);
const price = discount(50,0.10);
const price = discount(300,0.10);
从上面代码可以看出虽然商品价格不同,但是这些商品在促销活动中具有相同折扣率。
function discount(discount) {
return (price) => {
return price * discount;
}
}
const tenPercentDiscount = discount(0.1);
避免频繁使用相同值参数来调用函数
还是通过代码给大家解释一下,定义函数 volume 接受分别表示长、宽和高的 3 个参数 l, w, h 然后计算出其体积
function volume(l, w, h) {
return l * w * h;
}
这里虽然这里要计算体积时,对于高度取值都是 100,也就是上面说问题使用同一参数来频繁调用函数
volume(200,30,100) // 2003000l
volume(32,45,100); //144000l
volume(2322,232,100) // 53870400l
function volume(h) {
return (w) => {
return (l) => {
return l * w * h
}
}
}
定义一个特定函数 hCylinderHeight 用于缓存高度,这样就可以避免频繁使用相同参数来调用一个函数。
const hCylinderHeight = volume(100);
hCylinderHeight(200)(30); // 600,000l
hCylinderHeight(2322)(232); // 53,870,400l
Currying 一般的形式
function curry(fn, ...args) {
return (..._arg) => {
return fn(...args, ..._arg);
}
}
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
function sum(a, b, c) {
return a + b + c;
}
let curriedSum = curry(sum);
console.log( curriedSum(1, 2, 3) ); // 6, 每 currying 化的普通函数形式
console.log( curriedSum(1)(2,3) ); // 6, 部分 currying 化
console.log( curriedSum(1)(2)(3) ); // 6, 完全 curring 化
总结
在 JavaScript 中 currying 是基于闭包实现的,通过 currying 能够保留已经执行的函数的状态,使我们有能力创建工厂函数,能够为其参数添加特定值的函数。
对于初学者理解 currying 可能比较绕,只要理解了闭包,currying 其实并不难理解,只要稍加练习随后你变可以掌握这门编程思想,而且也会发现 currying 值得拥有。
参考文献
参考 Understand currying in Javascript blog
参考 curring blog