实现一个compose函数

实现函数组合工具,将多个函数从右到左依次执行,前一个函数的返回值作为后一个函数的参数

问题

compose函数是函数式编程中的重要概念,它可以将多个函数组合成一个函数。执行顺序是从右到左,即最右边的函数先执行,其返回值作为左边函数的参数,依次类推。

例如:compose(f, g, h)(x) 等价于 f(g(h(x)))

解答

/**
 * 实现compose函数 - 基础版本
 * @param  {...Function} fns 需要组合的函数列表
 * @returns {Function} 组合后的函数
 */
function compose(...fns) {
  // 边界情况:如果没有传入函数,返回一个恒等函数
  if (fns.length === 0) {
    return arg => arg;
  }
  
  // 如果只有一个函数,直接返回该函数
  if (fns.length === 1) {
    return fns[0];
  }
  
  // 使用reduce从右到左组合函数
  return fns.reduce((a, b) => (...args) => a(b(...args)));
}

/**
 * 实现compose函数 - 展开版本(更易理解)
 */
function compose2(...fns) {
  return function(...args) {
    // 从右到左执行函数
    let result = fns[fns.length - 1](...args);
    
    for (let i = fns.length - 2; i >= 0; i--) {
      result = fns[i](result);
    }
    
    return result;
  };
}

/**
 * 实现compose函数 - reduceRight版本
 */
function compose3(...fns) {
  if (fns.length === 0) {
    return arg => arg;
  }
  
  if (fns.length === 1) {
    return fns[0];
  }
  
  return function(...args) {
    // 使用reduceRight从右到左执行
    return fns.reduceRight((result, fn, index) => {
      // 最右边的函数可以接收多个参数
      return index === fns.length - 1 ? fn(...result) : fn(result);
    }, args);
  };
}

使用示例

// 示例1:基础使用
const add = x => x + 10;
const multiply = x => x * 2;
const subtract = x => x - 5;

const calculate = compose(add, multiply, subtract);
console.log(calculate(10)); // 20
// 执行过程:subtract(10) = 5 -> multiply(5) = 10 -> add(10) = 20

// 示例2:字符串处理
const toUpperCase = str => str.toUpperCase();
const exclaim = str => str + '!';
const split = str => str.split('');

const processString = compose(split, exclaim, toUpperCase);
console.log(processString('hello')); 
// ['H', 'E', 'L', 'L', 'O', '!']

// 示例3:数据处理管道
const users = [
  { name: 'Alice', age: 25, active: true },
  { name: 'Bob', age: 30, active: false },
  { name: 'Charlie', age: 35, active: true }
];

const getActiveUsers = users => users.filter(u => u.active);
const getNames = users => users.map(u => u.name);
const joinWithComma = names => names.join(', ');

const getActiveUserNames = compose(
  joinWithComma,
  getNames,
  getActiveUsers
);

console.log(getActiveUserNames(users)); // "Alice, Charlie"

// 示例4:数学运算
const double = x => x * 2;
const square = x => x * x;
const addOne = x => x + 1;

const compute = compose(square, double, addOne);
console.log(compute(3)); // 64
// 执行过程:addOne(3) = 4 -> double(4) = 8 -> square(8) = 64

关键点

  • 执行顺序:compose函数从右到左执行,这与数学中的函数组合概念一致 f(g(x))

  • reduce实现:使用reduce方法可以优雅地实现函数组合,是 (a, b) => (...args) => a(b(...args))

  • 参数传递:最右边的函数可以接收多个参数,中间函数只接收前一个函数的返回值

  • 边界处理:需要处理没有函数或只有一个函数的情况

  • 与pipe的区别:pipe函数是从左到右执行,compose是从右到左执行,两者互为镜像

  • 应用场景

    • 数据处理管道
    • 中间件机制(如Redux中间件)
    • 函数式编程中的代码复用
    • 提高代码可读性和可维护性
  • 性能考虑:每次调用都会创建新的函数闭包,对于高频调用场景需要注意性能优化