函数组合、结合律

函数组合

函数组合也叫饲养函数(compose)。

若干个纯函数、偏函数、柯里化函数组合成一个新的函数,形成数据传递,并实现一种有序执行的效果。

下面通过一个案例来看一下组合函数的作用。首先我们编写5个纯函数,分别有不同的功能。

function toUpperCase (str) {
  return str.toUpperCase(str);
}

function exclaim (str) {
  return str + '!';
}

function split (str) {
  return str.split(str);
}

function reverse (str) {
  return str.reverse();
}

function join (str) {
  return str.join();
}

下面我们定义compose函数,并使用此函数。

function toUpperCase (str) {
  return str.toUpperCase(str);
}

function exclaim (str) {
  return str + '!';
}

function split (str) {
  return str.split('');
}

function reverse (str) {
  return str.reverse();
}

function join (str) {
  return str.join();
}

function compose () {
  var args = [].slice.call(arguments),
      len = args.length - 1;

  return function (x) {
    var res = args[len](x);

    while (len--) {
      console.log(len, args, len, res);
      res = args[len](res);
    }

    return res;
  }
}

var comp = compose(join, reverse, split, toUpperCase, exclaim);
console.log(comp('HelloWolrd'));

也可以用ES6的reduce进行实现。

function compose () {
  const args = Array.prototype.slice.call(arguments);

  return function (x) {
    return args.reduceRight((res, cb) => cb(res), x);
  }
}

Pointfree

这种函数组合的方式是Pointfree编程风格的体现。Pointfree不使用所要处理的值,只合成运算过程。

Pointfree的本质就是使用一些通用的函数,组合成各种复杂运算。上层运算不需要直接操作数据,

而是通过底层函数处理。简单来说,Pointfree就是运算过程抽象化,处理一个值,但是不提到这个值。

有句英文这样说,Love means never having to say you’are sorry。

而Pointfree可以这样表述,Pointfree style means never having to say you data.

函数组合的实际应用

我们可以定义数据过滤的规则,用函数组合的方式进行数据过滤,获取我们想到的数据。

const data = [
  {"id": "1","course":"ReactJS+新闻头条实战","classes":"25","is_free":"0"},
  {"id": "2","course":"VueJS+美团实战","classes":"30","is_free":"1"}
]

function compose () {
  const args = Array.prototype.slice.call(arguments);

  return function (x) {
    return args.reduceRight((res, cb) => cb(res), x);
  }
}

function classesFilter (data) {
  return data.classes > 20 && data;
}

function freeFilter (data) {
  return data.is_free === '0' && data;
}

var arr = [],
    f = null;

data.forEach(item => {
  f = compose(freeFilter, classesFilter);
  f(item) && arr.push(f(item));
});

console.log(arr);