实现 compose 和 pipe 函数

手写函数组合 compose 和 pipe

问题

实现函数式编程中的 composepipe 函数,用于将多个函数组合成一个函数。

解答

实现 compose

compose 从右到左执行函数,即最后一个参数最先执行。

// 基础版本:使用 reduceRight
function compose(...fns) {
  // 没有函数时返回恒等函数
  if (fns.length === 0) return (arg) => arg;
  // 只有一个函数时直接返回
  if (fns.length === 1) return fns[0];

  // 从右到左依次执行
  return function (arg) {
    return fns.reduceRight((result, fn) => fn(result), arg);
  };
}

// 简洁版本
const compose2 = (...fns) =>
  fns.reduceRight(
    (prev, next) =>
      (...args) =>
        next(prev(...args)),
    (x) => x
  );

实现 pipe

pipe 从左到右执行函数,即第一个参数最先执行。

// 基础版本:使用 reduce
function pipe(...fns) {
  if (fns.length === 0) return (arg) => arg;
  if (fns.length === 1) return fns[0];

  // 从左到右依次执行
  return function (arg) {
    return fns.reduce((result, fn) => fn(result), arg);
  };
}

// 简洁版本
const pipe2 = (...fns) =>
  fns.reduce(
    (prev, next) =>
      (...args) =>
        next(prev(...args)),
    (x) => x
  );

使用示例

// 定义几个简单函数
const add10 = (x) => x + 10;
const multiply2 = (x) => x * 2;
const subtract5 = (x) => x - 5;

// compose: 从右到左执行
// subtract5(multiply2(add10(5))) = subtract5(multiply2(15)) = subtract5(30) = 25
const composedFn = compose(subtract5, multiply2, add10);
console.log(composedFn(5)); // 25

// pipe: 从左到右执行
// subtract5(multiply2(add10(5))) 变成 add10 -> multiply2 -> subtract5
// add10(5) = 15 -> multiply2(15) = 30 -> subtract5(30) = 25
const pipedFn = pipe(add10, multiply2, subtract5);
console.log(pipedFn(5)); // 25

支持异步的版本

// 异步 compose
const composeAsync =
  (...fns) =>
  (arg) =>
    fns.reduceRight((promise, fn) => promise.then(fn), Promise.resolve(arg));

// 异步 pipe
const pipeAsync =
  (...fns) =>
  (arg) =>
    fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(arg));

// 使用示例
const fetchUser = async (id) => ({ id, name: 'John' });
const getName = (user) => user.name;
const toUpperCase = (str) => str.toUpperCase();

const getUserName = pipeAsync(fetchUser, getName, toUpperCase);
getUserName(1).then(console.log); // 'JOHN'

关键点

  • compose 从右到左执行,pipe 从左到右执行
  • composereduceRightpipereduce
  • 处理边界情况:空函数数组返回恒等函数
  • 异步版本使用 Promise.resolve 包装初始值,用 .then 链式调用