1.2.函数式编程

134 阅读4分钟

函数式编程

What's Function Programming 什么是FP?

FP就像Object-oriented-Programming(OOP)一样,一种写代码的方法论,这些方法论告诉我们如何思考及解决问题

简单来FP核心思想就是做运算处理,并用function来思考问题,例如下面这个例子


(3+8) - 1*9

// 可以这样写

const add =(a,b)=> a + b

const mul = (a,b)=> a * b

const sub = (a,b) => a - b

sub(add(3,8),mul(1,9))

我们把每个运算包成一个个不同的function,并用这些function组合出我们想要的结果,这就是最简单的FP

FP Basic Condition 满足FP的基础条件

跟OOP 一样不是所有的语言都支持FP,要能够支持FP的语言至少要符合函数一等公民的特性

First Class 函数一等公民

一等公民简单来说函数能够被赋值给变数,函数也能够被当作参数传入另一个函数,也可以当作一个函数的返回值

函数能够赋值给变量

const hello = ()=> //...

函数能被当作参数传入

function timeoutHandler(){

//...

}

setTimeout(timeoutHandler)

函数能被当作值返回

const hello = () => ()=> //...

FP Important Features FP重要特性

FP 都是表达式(Expression),不会是陈述式(Statement)

表达式是一个运算过程,一定会有返回值,例如执行一个function


add(1,2)

陈述式则是表现某个行为,例如一个赋值给一个变量


const x = 1

有时候表达式也可能同时是合法的陈述式,这里只讲基本的判断方法,如果想更深入了解其中的差异,可以参考以下文章

2ality.com/2012/09/exp…

maksimivanov.com/posts/state…

由于FP最早就是为了做运算处理不管I/O,而Statement通常属于对系统I/O的操作,所以FP很自然的不会是Statement。

当然实际中不可能完全没有I/O的操作,FP只要求对I/O操作限制到最小,不要有不必要的I/O行为,尽量保持运算过程的单纯

Pure Function 纯函数

PF是指一个function给相同的参数,永远会返回相同的返回值,并且没有任何显著的副作用(Side Effect)


const arr = [1,2,3,4,5]

arr.slice(0, 3) // [1,2,3]

arr.slice(0, 3) // [1,2,3]

arr.slice(0, 3) // [1,2,3]

这里可以看到slice不管执行几次,返回值都是相同的,并且除了返回一个值(Value)之外并没有做任何事,所以slice就是一个pure function


const arr = [1,2,3,4,5]

arr.splice(0, 3) // [1,2,3]

arr.splice(0, 3) // [4,5]

arr.slice(0, 3) // []

这里我们换成用splice,因为splice每执行一次就会影响arr的值,导致每次结果都不同,这就很显不是一个pure function

Side Effect 副作用

Side Effect是指一个function做了跟本身运算返回值没有关系的事,比如说修改某个全局变量,或是修改传入参数的值,甚至是执行console.log都算是Side Effect

FP强调没有Side Effect,也就是function要保持纯粹,只做运算并返回一个值,没有其他额外的行为。

Referential Transparency 函数的引用透明性

上面提到的Pure Function不管外部环境如何,只要参数相同,函数行执行的返回结果必定相同。这种不依赖任何外部状态,只依赖于传入的参数的特性也称为引用透明(Referential Transparency)

利用参数保存状态

如果你使用过Redux应该会知Redux的状态是由各个reducer所组成的,而每个reducer的状态就是保存在参数中


function counter(state = 0, action){

// ....

}

如果你不熟悉reducer,可以看这个例子


function findIndex(arr, predicate, start = 0) {

if (0 <= start && start < arr.length) {

if (predicate(arr[start])) {

return start;

}

return findIndex(arr, predicate, start+1);

}

return -1

}

findIndex(['a', 'b'], x => x === 'b'); // 找数组中 'b' 的 index

这里的findIndex用来查找数组中的元素位置,我们在findIndex中故意多加了一个参数用来保存当前找到第个几index的状态,这就是利用参数保存状态!

FP Advantage FP 优势

可维护性

因为Pure Function等特性,执行结果不依赖外部状态,且不会对外部环境有任何操作,使Functional Programming能更好的除错及编写单元测试

可读性

当我们通过一系列的函数链式操作过程,代码能变得非常简洁,例如:


[2,1].concat([7,4,3,5]).sort().filter(x => x > 3) // 先升序然后过滤出大于3的数

易于并行/平行处理

FP易于做并行/平行(Concurrency/Parallel)处理,因为我们基本上只做运算不碰I/O,再加上没有Side Effect的特性,所以不用担心deadlock等问题