合成+柯里化

147 阅读3分钟

一、资源

函数式编程指南

阮一峰函数式编程入门教程

概念

函数式编程提倡 利用若干简单的执行单元 让计算结果不断渐进,逐层推导复杂的运算

函数式两个最基本的运算:合成+柯里化。

二、compose(合成)

如果一个值要经过多个函数,才能变成另外一个值,就可以把所有中间步骤合并成一个函数,这叫做"函数的合成"(compose)。

合成的好处:

1、代码简单富有可读性;

2、同时通过不同的组合方式,可以轻易组合出其他常用函数,让我们的代码更具表现力。

隐蔽规则:

有一个隐藏的前提,就是fg都只能接受一个参数。如果可以接受多个参数,比如f(x, y)g(a, b, c),函数合成就非常麻烦。 这时就需要函数柯里化了。所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。

function f1(arg) {
	console.log(arg+'f1');
	return arg;
}
function f2(arg) {
	console.log(arg+'f2');
	return arg;
}
function f3(arg) {
	console.log(arg+'f3');
	return arg;
}
function compose (...fns) {
	return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
compose(f1,f2,f3)('hello');


//hellof3
//hellof2
//hellof1
//"hello"

函数就像数据的管道(pipe)。那么,函数合成就是将这些管道连了起来,让 数据一口气从多个管道中穿过。

三、Currying(柯里化)

所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。

// 柯里化之前
function add(x, y) {
  return x + y;
}

add(1, 2) // 3

// 柯里化之后
function addX(y) {
  return function (x) {
    return x + y;
  };
}

addX(2)(1) // 3

实际使用:

const addx = (a)=>(b)=>(a+b);
const addOne = addx(1);
const addTen = addx(10);

const compose = (...fns)=>fns.reduce((a,b)=>(...args)=>a(b(...args)));

compose(addOne, addTen)(20);
// 31

四、函子

任何具有map方法的数据结构,都可以当作函子的实现。

class Functor {
  constructor(val) { 
    this.val = val; 
  }

  map(f) {
    return new Functor(f(this.val));
  }
}

上面代码中,Functor是一个函子,它的map方法接受函数f作为参数,然后返回一个新的函子,里面包含的值是被f处理过的(f(this.val))。

一般约定,函子的标志就是容器具有map方法。该方法将容器里面的每一个值,映射到另一个容器。

下面是一些用法的示例。

(new Functor(2)).map(function (two) {
  return two + 2;
});
// Functor(4)

(new Functor('flamethrowers')).map(function(s) {
  return s.toUpperCase();
});
// Functor('FLAMETHROWERS')

(new Functor('bombs')).map(_.concat(' away')).map(_.prop('length'));
// Functor(10)

上面的例子说明,函数式编程里面的运算,都是通过函子完成,即运算不直接针对值,而是针对这个值的容器----函子。函子本身具有对外接口(map方法),各种函数就是运算符,通过接口接入容器,引发容器里面的值的变形。

因此,学习函数式编程,实际上就是学习函子的各种运算。

由于可以把运算方法封装在函子里面,

所以又衍生出各种不同类型的函子,

有多少种运算,就有多少种函子。

函数式编程就变成了运用不同的函子,解决实际问题。

五、of


函数式编程一般约定,函子有一个of方法,用来生成新的容器。

Functor.of = function(val) {
  return new Functor(val);
};

然后,前面的例子就可以改成下面这样。

Functor.of(2).map(function (two) {
  return two + 2;
});
// Functor(4)

这就更像函数式编程了。