zlx362211854 / daily-study

每日一个知识点总结,以issue的形式体现
10 stars 6 forks source link

43. 介绍下函数式编程和柯里化 #81

Open zlx362211854 opened 5 years ago

zlx362211854 commented 5 years ago

函数

image 在数学中,函数定义为:对于一个输入x,总是能得到唯一的输出y,型为y=f(x)这样的对应关系,叫做函数。 编程中,纯函数也就是指的,严格按照y=f(x)来定义的函数,输出只由输入决定,且对于唯一的输入只有唯一的输出。 最简单的例子:

var add = (x) => x + 1

上面add函数,实现了最简单的+1操作,返回值由输入值x决定,且只有唯一的返回值。此时add函数就叫做纯函数。 什么是不纯的函数:

var max = 10
var add = (x) =>{
   return x+1 > max ? 0 : x+1
}

add 返回值由外部变量max和输入值x同时决定,当输入值x+1>max时,返回值变成了0,这样返回值与输入值x不是唯一映射关系了,这样的函数就不是纯函数。

柯里化

柯里化可以看作函数的预存储或者预加载:

var add = (x, y) => x + y

// 这是一个基本加法计算的函数
add(2, 1) // 3
add(2, 4) // 6
add(2, 10) // 12
// 可以看到,我求的都是2加上一个数字的结果,每次都要输入参数2。
// 如何将它柯里化:
var add = (x) => (y) => x + y
var a2 = add(2)//将2预先加载出来
a2(1) // 3
a2(4) // 6
a2(10) // 12

柯里化后,可以将a2预加载出来,后续只要是要求2加上任何数字,都可以直接调用a2来计算,这里的a2,就是2的预加载函数。

nanslee commented 5 years ago
goldEli commented 5 years ago

函数式编程的核心思想就是:申明式编程(Declarative programming)

与命令式编程不一样,不用去关心是怎么实现的,而关心实现了什么,比较典型的申明式编程: HTMLJSX等。

以画只猫为例子:

// 画猫
drawCat(
    // 画头
    drawHead,
    // 画身体
    drawBody,
    // 画尾巴
    drawTail,
)(blankPaper) // 传入一张白纸

drawCat 接收一张白纸,drawHead 在白纸上画头,然后把画有头的白纸传给 drawBody,画好身体后,又传给 drawTail。

这样写有两个好处:

很好的阅读性:

整个代码看起来就像读记述文一样,很容易就明白这段代码做了什么。具体复杂的实现,都封装到了drawHead,drawBody,drawTail这个三个纯函数里面。

非常灵活扩展性和复用性:

drawHead, drawBody, drawTail,都是纯函数,没有任何副作用,可以用 drawCat 随意组合。 如果不想要尾巴,想给猫咪画个帽子,直接删除 drawTail 这个方法,再加一个 drawHat 的纯函数。

// 画猫
drawCat(
    // 画头
    drawHead,
    // 画身体
    drawBody,
    // 画尾巴
    // drawTail,
    // 画帽子
    drawHat,
)(blankPaper) // 传入一张白纸

如何实现?

实现函数式编程的三个关键就是:纯函数组合函数柯里化

纯函数

相同的输入永远得到相同的输出,也就是没有任何副作用。

组合函数

drawCat 就是一个组合函数,将多个纯函数组装起来,分别执行,第一个纯函数执行返回的结果,是第二个纯函数的输入参数,以此类推。最终得到一张画了猫的白纸。

react-reduxcombineReducer 就是一个非常典型的组合函数。

柯里化

上面的例子,实现了 point-free,也就是无参数化,除了 blankPaper,看不到任何其他参数,剥离任何会影响代码阅读的因素。

为了实现无参数化,就需要用柯里化对函数的的参数进行封装。

以 drawHat 为例子,它除了有 blankPaper 这个参数外,还会有颜色(color),大小(size)等参数。

const drawHat = (color, size, blankPaper) => {
    // some code to draw hot
    return blankPaper
}

// drawHat 柯里化版本
// 可以用lodash提供的柯里化方法 _.curry
const drawHat = _.curry((color, size, blankPaper) => {
    // some code to draw hot
    return blankPaper
})(color, size)

这样 drawHat 就只用接受 blankPaper 这一个参数,实现 drawCat 的无参数化。