Libon

Function Compose / Associativity / Pointfree

3Mins #JavaScript
函数组合、结合律和 Pointfree 的概念

ToC

核心概念

函数组合,也叫饲养函数(compose)。为什么会叫饲养函数?可以理解为将其他的小的纯函数、偏函数、柯里化函数喂养给最后的一个大的功能函数,让这个功能变膘变壮,以完成最后的功能。举个例子:

1
// 传统方式
2
function toUpperCase(str) {
3
return str.toUpperCase()
4
}
5
6
function exclaim(str) {
7
return str + '!'
8
}
9
10
// 传统调用方式
5 collapsed lines
11
console.log(exclaim(toUpperCase('hello'))) // HELLO!
12
13
// 使用 compose 的组合方式:
14
const composing = compose(exclaim, toUpperCasem) // 返回一个新的函数
15
composing('hello') // 调用组合好的函数集合

对比直接调用的方法,函数组合的好处是跟容易进行函数复用,而 compose 函数的定义如下:

1
function compose(f, g) {
2
return function(x) {
3
return f(g(x))
4
}
5
}

很简单的一个函数对不对,实际上核心的代码是第三行的 f(g(x)) ,它通过自右向左组合函数并调用最后传入参数执行,得到最终的效果,这种组织代码的方式,被叫做 左倾 。简单来讲,左倾 就是将一个函数的执行放在另一个函数调用的 () 中,将执行的函数返回值作为下一个执行函数的参数。以上 compose 函数虽然能满足需求,但是如果想要组合的函数再多一个的话,它就不得不进行修改了,所以这肯定不是最终方案。

改造函数

1
function compose(...fns) {
2
// 返回一个函数,并将接收到的函数传给最初传入的函数列表,函数列表会从右向左执行函数,最终返回最后一个函数的返回值
3
return (value) => fns.reduceRight((params, fn) => fn(params), value)
4
}

我们再对这个函数传入更多函数进行测试:

1
function toUpperCase(str) {
2
return str.toUpperCase()
3
}
4
5
function exclaim(str) {
6
return str + '!'
7
}
8
9
function split(str) {
10
return str.split('')
12 collapsed lines
11
}
12
13
function reverse(arr) {
14
return arr.reverse()
15
}
16
17
function join(arr) {
18
return arr.join('')
19
}
20
21
const composing = compose(exclaim, toUpperCase, join, reverse, split)
22
console.log(composing('hello')) // OLLEH!

结合律(Associativity)

结合律在 JS 编程中同样是一个比较少见的概念,它与数学中的加法结合律概念一致

加法结合律:三个数相加,先把前面两个数相加,再加第三个数,或者先把后面两个数相加,再和第一个数相加,它们的和不变。

1
const c1 = compose(join, reverse, split)('hello') // 'olleh'
2
const c2 = compose(compose(join, reverse), split)('hello') // 'olleh'
3
const c3 = compose(join, compose(reverse, split))('hello') // 'olleh'

结果如上,不管将他们的值怎么组合,只要函数传入的顺序不变,那么最终的结果也是不会变的。

Pointfree

Love means never having to say you’re sorry.
爱意味着永远不用说抱歉。

Pointfree style means never having to say your data.
pointfree 意味着你永远不用主动表明你的数据。


CD ..