#27 compose 函数


  • 0
    administrators

    在函数式编程当中有一个很重要的概念就是函数组合,实际上就是把处理数据的函数像管道一样连接起来,然后让数据穿过管道得到最终的结果。例如:

    const add1 = (x) => x + 1
    const mul3 = (x) => x * 3
    const div2 = (x) => x / 2
    
    div2(mul3(add1(add1(0)))) // => 3
    

    而这样的写法可读性明显太差了。我们可以构建一个 compose 函数,它接受任意多个函数作为参数(这些函数都只接受一个参数),然后 compose 返回的也是一个函数,达到以下的效果:

    const operate = compose(div2, mul3, add1, add1)
    operate(0) // => 相当于 div2(mul3(add1(add1(0))))
    operate(2) // => 相当于 div2(mul3(add1(add1(2))))
    

    简而言之:compose 可以把类似于 f(g(h(x))) 这种写法简化成 compose(f, g, h)(x)。请你完成 compose 函数的编写。

    额外挑战:你能通过 1~2 行代码实现 compose 吗。


  • 2

    分享一个一行代码实现的方法

    const compose = (...fns) => {
      return x => fns.reduceRight((v, f) => f(v), x);
    }
    

  • 0
    administrators

    @ackerMan 👍


  • 1
    管理员

    @胡子大哈 尝试用递归的思想解,后台提示“args.pop(...) is not a function”。。本地跑好像没问题,不可以用pop函数吗?

    const compose = (...args) => {	
        return x => {
    	let re = args.pop()(x)
    	 return args.length? compose(...args)(re): re
        }
    }
    

  • 0
    administrators

    @陈小俊 compose()(1) 考虑不传入函数的情况,应该原封不动返回数据。


  • 0
    administrators

    @陈小俊 其实 compose 应该最少有一个参数,这里测试用例也没有必要太苛刻了,我改一下。你试一下应该可以通过测试了。


  • 0
    管理员

    @胡子大哈 在 #27 compose 函数 中说:

    @陈小俊 compose()(1) 考虑不传入函数的情况,应该原封不动返回数据。

    好的哈,通过了


  • 0

    @ackerMan 赞一个~,在只接受一个参数的前提下确实优雅~


  • 0

    const compose = (...rest) => {
      return (parameter) => {
        for(let i = rest.length - 1; i>=0 ;i--){
          parameter = rest[i](parameter) 
        }
        return parameter
      }
    }
    

    O__O "…


  • 0

    支持多参数,但是必须至少传一个函数...

    const compose = (...fns) => fns.reduceRight((acc, fn) => (...args) => fn(acc(...args)))
    

  • 0

    const compose = (...funs) => (i) => funs.reduceRight((pre, cur) => cur(pre), i)

  • 0

    @ackerMan

    const compose = (...funs) => (i) => funs.reduceRight((pre, cur) => cur(pre), i)

  • 0

    const compose = () => {
      /* TODO */
      var arr = Array.prototype.slice.call(arguments);
      var now = arr.reduce((prev,cur,index)=>{
        return prev(cur);
      })
      return (conts)=>{
        now(conts)
      }
    }
    
    

    想问下是那里错了?提示:

    Runtime Error
    Reduce of empty array with no initial value
    

  • 0

    const compose = (...fns) => fns.reduce((a, b) => (...args) => a(b(...args)), x => x)
    
    // or
    
    const compose = (...fns) => fns.reduceRight((a, b) => (...args) => b(a(...args)), x => x)
    

  • 0

    const compose = (...arg) => {
      return (x) => {
        let res = arg.pop()
        if (Object.prototype.toString.call(res) === "[object Function]") {
          res = res(x)
        } else {
          return false
        }
        while (arg.length) {
          res = arg.pop()(res)
        }
        return res
      }
    }
    

  • 0

    一行

    const compose = (...fns) => (args) => fns.reduceRight((args, fn) => fn(args), args)
    

    多个参数

    const compose = (...fns) => (...args) => fns.reduceRight((args, fn) => fn(...[].concat(args)), [].concat(args))
    

  • 0

    const compose = function(){
    var args=[].slice.call(arguments).reverse()
    return function(){
    var fn=null
    var arg=arguments[0]
    for(var i=0;i<args.length;i++){
    fn=args[i].call(null,arg)
    arg=fn
    }
    return fn
    }
    }
    不使用箭头函数的写法


  • 0

    类似于中间件思想,将函数的返回值作为下一个函数的参数,这里需要注意是传入的函数的执行顺序是反的,即从右向左执行,因此考虑使用JS Array的reduceRight进行执行:

    const compose = (...args) => {
      let fns = args
      return val => {
        let firstFn = fns.pop() // 第一个执行的函数作为reduceRight的初始值
        let result = fns.reduceRight((sum, currentFn) => {
          return currentFn(sum)
        }, firstFn(val))
        return result
      }
    }
    

    看了@ackerMan 的一行实现,只能感叹简洁之美


  • 0

    @xiangshu reduce第二个参数作为初始值,你这里没传


登录后回复
 

与 ScriptOJ 的连接断开,我们正在尝试重连,请耐心等待