Libon

Function Compose / Associativity / Pointfree

函数组合、结合律和 Pointfree 的概念

在学习完纯函数之后,函数式编程就已经开始慢慢成型了,因为函数式编程本质上就是将各种纯函数组合在一起来实现功能,而本篇则是对纯函数的一层封装,以一种特性的组合方式将所有传入的纯函数列表进行组合调用,从而实现对函数式编程的另一种理解。

核心概念

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

// 传统方式
function toUpperCase(str) {
	return str.toUpperCase()
}

function exclaim(str) {
	return str + '!'
}

// 传统调用方式
console.log(exclaim(toUpperCase('hello'))) // HELLO!

// 使用 compose 的组合方式:
const composing = compose(exclaim, toUpperCasem) // 返回一个新的函数
composing('hello') // 调用组合好的函数集合

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

function compose(f, g) {
	return function(x) {
		return f(g(x))
	}
}

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

改造函数

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

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

function toUpperCase(str) {
	return str.toUpperCase()
}

function exclaim(str) {
	return str + '!'
}

function split(str) {
	return str.split('')
}

function reverse(arr) {
	return arr.reverse()
}

function join(arr) {
	return arr.join('')
}

const composing = compose(exclaim, toUpperCase, join, reverse, split)
console.log(composing('hello')) // OLLEH!

结合律(Associativity)

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

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

const c1 = compose(join, reverse, split)('hello') // 'olleh'
const c2 = compose(compose(join, reverse), split)('hello') // 'olleh'
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 ../