JavaScript 14 天编程挑战
请你编写一个函数,它接收一个函数数组 [f1, f2, f3,…], fn]
,并返回一个新的函数 fn
,它是函数数组的复合函数 。
[f(x), g(x), h(x)]
的 复合函数 为 fn(x) = f(g(h(x)))
。
一个空函数列表的 复合函数 是 恒等函数 f(x) = x
。
你可以假设数组中的每个函数接受一个整型参数作为输入,并返回一个整型作为输出。
type F = (x: number) => number;
function compose(functions: F[]): F {
if (functions.length === 0) {
return (x: number) => x;
} else if (functions.length === 1) {
return functions[0];
}
return functions.reduce(
(a, b) => (x: number) => a(b(x))
);
};
/**
* const fn = compose([x => x + 1, x => 2 * x])
* fn(4) // 9
*/
/**
* @param {Function[]} functions
* @return {Function}
*/
var compose = function(functions) {
if (functions.length === 0) {
return (x) => x;
} else if (functions.length === 1) {
return functions[0];
}
return functions.reduce(
(a, b) => (x) => a(b(x))
);
};
/**
* const fn = compose([x => x + 1, x => 2 * x])
* fn(4) // 9
*/
题目默认模版提供的这个符合函数名称叫作 compose
,在前端领域,我所知道的 compose
有两处知名的应用,redux
和 koa
的中间件。
本题的要求和解法,更接近于 redux
里的写法,我们可以参考:redux
正如题目里所说,当空函数时,我们直接返回一个函数:(x) => x
。
当函数只有一个时,也就不需要复合,直接返回该函数。
由于题目要求函数列表的执行顺序实际是从右向左计算的,我们可以利用 Array.prototype.reduce(callbackFn, initialValue?)
。
当 initialValue
缺省时,会从数组中的第一个值作为 callbackFn
的一个参数,第二个参数则是数组的第二个值。
所以,如果函数分别是 [f1, f2, f3]
时,我们的函数是这么执行的:
第一次循环,a
是 f1
, b
是 f2
, 返回 (x) => f1(f2(x))
;
下一次循环时,a
是 (x) => f1(f2(x))
, b
是 f3
, 返回 (x) => f1(f2(f3(x)))
,有点递归的意思。
即,下一次的执行结果作为上一次的参数。由此可见,最早执行的函数反而是函数组数中最右边的函数。
上面的分析,可能读者会不太明白,其实还是 reduce
函数不是很好理解造成的,我们完全可以改成 for
循环的形式,从后往前执行亦可,每次依然是把执行结果作为下一次循环的入参数。
由于 redux
的源码就是使用的 redece
去写的,所以还是推荐理解一下 reduce
的写法。
同样地,我们可以利用 Array.prototype.reduceRight(callbackFn, initialValue?)
实现,此方法和 reduce
相似,差别是会从右向左应用函数,虽然代码上几乎没有区别,但是理解起来更加自然。
本文的目的是希望能巩固 redux
里 compose
函数的写法,但是还是提供一下比较简单的代码:
type F = (x: number) => number;
function compose(functions: F[]): F {
return functions.reduceRight(
(a, b) => (x: number) => b(a(x)),
x => x,
);
};
/**
* const fn = compose([x => x + 1, x => 2 * x])
* fn(4) // 9
*/
/**
* @param {Function[]} functions
* @return {Function}
*/
var compose = function(functions) {
return functions.reduceRight(
(a, b) => (x) => b(a(x)),
(x) => x,
);
};
/**
* const fn = compose([x => x + 1, x => 2 * x])
* fn(4) // 9
*/