Daily-Question icon indicating copy to clipboard operation
Daily-Question copied to clipboard

【Q181】如何实现 compose 函数,进行函数合成

Open shfshanyue opened this issue 5 years ago • 10 comments

实现一个 compose 函数,进行函数合成,比如 redux 中的 compose,react 高阶组件连续调用时的 compose

const add10 = x => x + 10
const mul10 = x => x * 10
const add100 = x => x + 100

// (10 + 100) * 10 + 10 = 1110
compose(add10, mul10, add100)(10)

shfshanyue avatar Jan 16 '20 15:01 shfshanyue

function compose() {

  let fns = [...arguments];

  return function() {
    let args = [...arguments];
    let result = fns.reduce((ret, fn) => {
      ret = fn.apply(this, ret);
      return Array.isArray(ret) ? ret : [ret];
    }, args);
    
    return result;
  }
}

let toUpperCase = (x) => x.toUpperCase();
let exclaim = (x) => x + '!';
let shout = compose(toUpperCase,exclaim);
let str = shout('hello world');
console.log(str);

第一次试着写,简单查了一下compose是什么意思,竟然拼凑出来了。请各位指正。 :smile:

RainMaker-Q avatar Jul 21 '20 02:07 RainMaker-Q

看到一种新实现

function compose(...fns) {
  fns.reduce( (a, b) => (...args) => a(b(...args)));
}

RainMaker-Q avatar Jul 28 '20 01:07 RainMaker-Q

const compose = (...fns) => (...params) => fns.reduce((prev, current) => current(prev), ...params);

nieyao avatar Nov 30 '20 16:11 nieyao

实现一个从右向左(right to left)计算的组合函数:

const compose = (...fns) =>
  // 注意 f、g 的位置,如果实现从左到右计算,则置换顺序
  fns.reduce((f, g) => (...args) => f(g(...args)))

跑一个示例

const add5 = x => x + 5
const multiply = (x) => x * 10
const multiply10AndAdd5 = compose(
  add5,
  multiply
)
multiply10AndAdd5(10) // 105

Redux 中广泛使用了 compose 函数,其中的实现如下

export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return <T>(arg: T) => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
}

shfshanyue avatar Dec 28 '20 02:12 shfshanyue

@nieyao params 应该置后,测试示例时没有通过

shfshanyue avatar Dec 28 '20 02:12 shfshanyue

@nieyao params 应该置后,测试示例时没有通过

确实,忘了compose是从右往左执行的,那加个reverse 好了。 const compose = (...fns) => (...params) => fns.reverse().reduce((prev, current) => current(prev), ...params) 或者用reduceRight const compose = (...fns) => (...params) => fns.reduceRight((prev, current) => current(prev), ...params)

nieyao avatar Dec 28 '20 03:12 nieyao

function compose(...fn) {
  return (...args) => fn.reverse().slice(1).reduce((acc, cur) => cur(acc), fn[0](...args))
}

Kiera569 avatar Jul 19 '21 07:07 Kiera569

这种方式更优雅

const compose = (...fns) =>
  fns.reduceRight(
    (prev, next) =>
      (...args) =>
        prev(next(...args))
  );

maolovecoding avatar Jul 04 '22 07:07 maolovecoding

之前看了koa的compose实现,先入为主了,感觉写的没上面的优雅,不过能过用例:

function compose(...args) {
  const length = args.length;
  function dispatch(index, val) {
    if (index === length) return val;
    else {
      args = args || [];
      return args[index](dispatch(index + 1, val));
    }
  }
  return (init) => dispatch(0, init);
}

kirazZ1 avatar Mar 09 '23 08:03 kirazZ1

 const compose = (...fns) => ((params) => fns.reduce((prev, cur) => (prev = cur(prev ?? params), prev), null))

看了大佬的实现,感觉我的实现突然不香了

Hazel-Lin avatar Jul 04 '23 06:07 Hazel-Lin