函数组合

管道(pipeline)

在 linux 系统中,大家一定使用过管道(pipe):

cat test.sh | grep -n 'echo'

| 左边命令的输出将会作为右边命令的输入,这就构成了一条流水线:

dataAdataBfuncA(data)funcB(dataA)funcC(dataB)result

在该视角下,我们通过不同处理器不断处理数据,并产出新的数据。管道并不是一种技术实现,而是一种技术机制,下面我们在 JavaScript 中实现上面的流水线机制:

function pipeline() {
  // 传入pipleline的会是一系列函数,亦即流水线上各个工段的加工期
  var funcs = Array.prototype.slice.call(arguments, 0, arguments.length-1);
  var datas = Array.prototype.slice.call(arguments, arguments.length-1);
  return funcs.reduce(function(result, func) {
    return func(result);
  }, datas);
}

// 测试
function A(data) {
  return 2*data;
}

function B(data) {
  return -data;
}

function C(data) {
  return data+3;
}

pipeline(A,B,C,2); // => -1

再考虑另外一种流水线视角:

funcBfuncABfuncC

相比于第一种流水线视角,在第二种视角中,我们不再不断加工数据,而是不断组合我们的处理器,产生新得处理器:

function pipe() {
  var funcs = Array.prototype.slice.call(arguments);
  return function() {
    var result = Array.prototype.slice.call(arguments);
    return funcs.reduce(function(result, func, index) {
      if(index === 0 ) {
            return func.apply(this, result);
      }
      return func.call(this, result);
    }, result);
  }
}

//测试
var newFunc = pipe(A, B, C);
newFunc(2); // => -1

_.compose

_.compose(..funcs):组合各个函数为一新函数。

underscore 也为我们提供了组合函数的 API -- _.compose

_.compose = function () {
    var args = arguments; // 函数序列
    var start = args.length - 1; // 以传入的最后一个函数作为首个处理器
    return function () {
        // 逐个执行函数,获得结果
        var i = start;
        var result = args[start].apply(this, arguments);
        while (i--) result = args[i].call(this, result);
        return result;
    };
};

与我们撰写的 pipe 不同的是,underscore 提供的 _.compose 接受函数的顺序与执行组合顺序是相反的。在函数式编程中,从右向左的组合顺序是标准:

var newFunc = _.compose(C, B ,A);
newFunc(2); // => -1

参考资料

results matching ""

    No results matching ""