函数式编程

200 阅读3分钟
  1. 为什么函数式编程?

函数式编程起源于数学中的范畴论。范畴就是使用箭头连接的物体,也就是说,彼此之间存在某种关系的概念、事物、对象等等,都构成"范畴"。随便什么东西,只要能找出它们之间的关系,就能定义一个"范畴"。

image.png

上图中,各个点与它们之间的箭头,就构成一个范畴。范畴论认为,同一个范畴的所有成员,就是不同状态的"变形"(transformation)。通过"态射",一个成员可以变形成另一个成员。范畴论使用函数,表达范畴之间的关系。伴随着范畴论的发展,就发展出一整套函数的运算方法。这套方法起初只用于数学运算,后来有人将它在计算机上实现了,就变成了今天的"函数式编程"。

本质上,函数式编程只是范畴论的运算方法,跟数理逻辑、微积分、行列式是同一类东西,都是数学方法,只是碰巧它能用来写程序。

函数式编程可以总结为:

  • 与面向对象编程(Object-oriented programming)和过程式编程(Procedural programming)并列的编程范式。
  • 最主要的特征是,函数是第一等公民
  • 强调将计算过程分解成可复用的函数,典型例子就是map方法和reduce方法组合而成 MapReduce 算法
  • 只有纯的、没有副作用的函数,才是合格的函数。
  1. 怎么使用函数式编程? 函数式编程主要有5个特征
  • 不可变性
  • 纯函数(pure function)
  • 高阶函数
  • 柯里化(curry)
  • 组合

2.1 不可变性

不可变性指在在函数式编程中,你无法更改数据,也不能更改。 如果要改变或更改数据,则必须复制数据副本来更改。

2.1 纯函数

纯函数指没有副作用的函数,没有副作用就是不会产生对外部可观察的变化,如改变全局变量值 / 对DOM进行修改等等。相同的输入有相同的输出。

2.2 数据转换

JS中不内置的方法不改变原数组,返回新的数组或对象。

concat() join() slice() toString()

2.3 高阶函数

高阶函数指将函数作为参数或者返回函数的函数。

Array.mapArray.filterArray.reduce等等

2.4 柯里化

curry 的概念:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

var add = function(x) {
  return function(y) {
    return x + y;
  };
};

var increment = add(1);
var addTen = add(10);

increment(2);
// 3

addTen(2);
// 12

手写一个curry

function curry(){
    let args = arguments
    return function(){
        let args_ = [...args, ...arguments]

        console.log(args_.reduce((prev,cur)=>prev+cur,0))
    }
}

curry(2)(3,2,3,15)
// result: 25

2.4 代码组合

var compose = function(f,g) {
  return function(x) {
    return f(g(x));
  };
};

上面的代码就是组合,组合看起来像是在饲养函数。你就是饲养员,选择两个有特点又遭你喜欢的函数,让它们结合,产下一个崭新的函数。组合的用法如下:

var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);

shout("send in the clowns");
//=> "SEND IN THE CLOWNS!"

手写compose

function compose() {
    let func = [...arguments]

    return func.reduce((prev, cur) => (...arguments) => {
        return cur(prev(...arguments))
    })
}

function fn1(x) {
    return x + 1;
}
function fn2(x) {
    return x + 2;
}
function fn3(x) {
    return x + 3;
}
function fn4(x) {
    return x + 4;
}

const a = compose(fn1, fn2, fn3, fn4);

console.log(a(22))
// result: 32