【问题标题】:piping functions in JavaScriptJavaScript 中的管道函数
【发布时间】:2016-10-12 20:23:42
【问题描述】:

我怎样才能拥有一个 JavaScript 函数,比如说 piper(),它接受多个函数作为其参数,并返回一个新函数,该函数将其参数传递给第一个函数,然后将结果传递给第二个函数,然后 将第二个的结果传递给第三个,依此类推,最后返回最后一个函数的输出。

piper(foo, fee, faa)(10, 20, 30) 这样的东西相当于调用faa(fee(foo(10,20,30)))

ps: 这是我几天前做的一次采访的一部分。

【问题讨论】:

  • 你自己有没有尝试过?
  • @MikeC 我习惯在js中看到这样的函数 ali( saberi,i) {saberi(i)} ,不是这样的。带2个括号
  • @AliSaberi 请记住:您可以从函数返回函数。 See this question.
  • 就像很多面试问题一样,如果您曾经编写过这样的代码,那么您应该通过代码审查。这绝对是疯了。 f(...)(...)(...) 根本不可读。作为一项学术练习,提示在您的问题中:它“返回一个新功能”。

标签: javascript ecmascript-6


【解决方案1】:

对于任意数量的函数,您可以使用这个 ES6 函数:

function piper(...fs) {
    return (...args) => fs.reduce((args,f) => [f.apply(this,args)],args)[0];
}
// Example call:
var result = piper(Math.min, Math.abs, Math.sqrt)(16, -9, 0)
// Output result:
console.log(result);

在 ES5 语法中相同:

function piper(/* functions */) {
    var fs = [].slice.apply(arguments);
    return function (/* arguments */) { 
        return fs.reduce(function (args,f) {
            return [f.apply(this,args)];
        }.bind(this), [].slice.apply(arguments))[0];
    }.bind(this);
}
// Example call:
var result = piper(Math.min, Math.abs, Math.sqrt)(16, -9, 0)
// Output result:
console.log(result);

【讨论】:

  • f(...args) 会更短,但我喜欢你让它保留上下文。
  • 所以基本上,这是 ES6 的问题和解决方案,对吧?
  • @Vic 是对的。这不是 ES6 problem(有这种事吗?),而是 ES6 语法中表示的解决方案。
  • 添加 ES5 等同于我的答案。
  • ES6 解决方案将 this 锁定到全局范围,因为箭头功能。这不好。 ES5 示例泄漏 arguments,这会取消优化函数。否则,没关系。
【解决方案2】:

享受。纯 ES5 解决方案。保留this

function piper(){
    var i = arguments.length,
        piped = arguments[ --i ];

    while( --i >= 0 ){
        piped = pipeTwo( arguments[ i ], piped );
    }

    return piped;
}

function pipeTwo( a, b ){
    return function(){
        return a.call( this, b.apply( this, arguments ) );
    }
}

或者,如果你想要花哨的解决方案。

function piperES6( ...args ){
    return args.reverse().reduce( pipeTwo );
}

循环可以根据所需的方向反转。

【讨论】:

    【解决方案3】:

    @trincot's answer 非常相似(保留上下文),但以正确的顺序组合并且速度稍快,因为它不创建中间数组:

    const piper = (...steps) => function(...arguments) {
      let value = steps[0].apply(this, arguments);
      for (let i = 1; i < steps.length; ++i) {
        value = steps[i].call(this, value);
      }
      return value;
    };
    
    
    
    // Usage:
    
    let p = piper(
      x => x + 1,
      x => x * 2,
      x => x - 1
    );
    
    console.log(p(2)); // 5

    【讨论】:

      【解决方案4】:

      这是涉及方法链接的替代答案。我将使用 ES6,当然这可以转换为 ES5。这个解决方案的好处是它有一个非常简洁的 TypeScript 对应物,具有完美的可键入性。

      class Pipe {
          constructor(value) {
              this.value = value;
          }
          then(f) {
              return new Pipe(f(this.value));
          }
      }
          
      const pipe = value => new Pipe(value);
          
      // Example
      
      const double = x => 2 * x;
          
      pipe(42).then(double).then(console.log); // 84
      
      const result = pipe(42).then(double).then(double).value;
      console.log(result); // 168

      【讨论】:

        【解决方案5】:

        一个基于JS高阶函数用法的简单解决方案:

        function pipe(...rest) {
          return x => rest.reduce((y, f) => f(y), x);
        }
        
        

        用法:

        pipe((a) => a + 1, (a) => a * 2)(3) // 8
        pipe((a) => a + 1, (a) => a * 2)(2) // 2
        

        【讨论】:

          【解决方案6】:
          function f(f1, f2, f3){
          
              return (args => f3(f2(f1(args))));
          
          }
          

          【讨论】:

          • 不适用于任意数量的参数。与其中两个一起使用时引发异常。面试失败;)
          • 我举了一个有 3 个函数的例子,但是我们可以传递一个函数数组。
          【解决方案7】:

          我认为你正在尝试做的是链接。

          var funct={
              total:0,
              add:function(a) {
                  console.log(funct.total,funct.total+a);
                  funct.total+=a;
          
                  return funct;
              }
          };
          
          
          funct.add(5).add(6).add(9);
          

          【讨论】:

          • 方法必须这样调用:piper(foo, fee, faa)(10, 20, 30)
          猜你喜欢
          • 1970-01-01
          • 2019-11-18
          • 1970-01-01
          • 2018-01-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-14
          • 1970-01-01
          相关资源
          最近更新 更多