函数组合
需求
我们需要写一个函数,输入“blue”,返回“HELLO BLUE ”。
尝试
1 | var toUpperCase = function(x) {return x.toUpperCase();}; |
目前的步骤较少,所以嵌套的不是很深,如果有更多的操作,greet函数就要更多的嵌套,就类似与于fn3(fn2(fn1(fn0(x))))了。下面就来进行优化
优化
这里写一个compose函数:
1 | var compose = function(f,g) { |
利用compose将两个函数组合成一个函数,让代码从右往左运行,而不是由内而外运行,可读性大大提升。者就是函数组合。
但是现在compose函数也只是能支持两个参数,如果有更多的步骤呢?最后好像和前面说的嵌套又没有什么区别了compose(d, compose(c, compose(b, a)))
compose
这里是underscore的compose函数的实现:
1 | var toUppCase = function(x) {return x.toUpperCase()} |
pointfree
这里我们来了解一个概念叫做pointfree
pointfree指的是函数无须提及将要操作的数据是什么样的。
以最初的需求为例:
1 | // 需求: 输入'blue',返回 'HELLO BLUE'。 |
这里结合curry函数写一个更加复杂的例子。
1 | // 需求: 输入'blue is good man', 返回 'B.I.G.M' |
从这个例子中我们可以看到,利用柯里化(柯里化)和函数组合(compose)非常有助于实现pointfree
尽管感觉这个东西好像是很厉害的样子,但是写起来真是麻烦,还要写这么多的基础函数…..,但是如果有工具库已经帮你写好了呢?比如ramda.js
1 | // 使用 ramda.js |
而且你也会发现:
Pointfree的本质就是使用一些通用的函数,组合出各种复杂运算。上层运算不要直接操作数据,而是通过底层函数去处理。即不适用所要处理的值,只合成运算过程。
那么使用pointfree模式究竟有什么好处呢?
pointfree 模式能够帮助我们减少不必要的命名,让代码保持简洁和通用,更符合语义,更容易复用,测试也变得轻而易举。
实战
假设我们从服务器获取这样的数据
1 | var data = { |
下面使用到的curry函数
1 | function sub_curry(fn) { |
我们需要写一个 getIncompleteTaskSummaries
的函数,接收一个 username
作为参数,从服务器获取数据,然后筛选出这个用户的未完成的任务的 ids、priorities、titles、和 dueDate
数据,并且按照日期升序排序。
以 Scott
为例,最终筛选出的数据为:
1 | [ |
普通方式为:
1 | // 第一版 过程式编程 |
如果使用 pointfree 模式:
1 | // 第二版 pointfree改写 |
如果直接使用 ramda.js,你可以省去编写基本函数:
1 | //第三版使用 ramda.js |
当然了,利用 compose,你也可以这样写:
1 | // 第四版 使用 compose |
compose 是从右到左依此执行,当然你也可以写一个从左到右的版本,但是从右向左执行更加能够反映数学上的含义。
ramda.js 提供了一个 R.pipe 函数,可以做到从左到右,以上可以改写为:
1 | // 第五版 使用 R.pipe |