学习记录-函数式编程-函数组合

414 阅读3分钟

函数组合

  • 纯函数与柯里化使函数的粒度变细,很容易写出嵌套的代码,如f(g(p(x))),即函数的输出为另一个函数的输入,想象一下函数蜈蚣。。。
  • 将细粒度的函数重新组合成一个新的函数,该函数接收任意数量的细粒度函数,返回一个函数接收细粒度函数运行的初始参数;
//函数组合演示
//用于组合的函数的运行顺序为从右到左,即最右函数运行后的输出为次右函数的输入,以此类推
//组合函数
function compose(f, g){
    return function(value){
        return f(g(value));
    }
}
//细粒度函数
function reverse(array){
    return array.reverse();
}
function first(array){
    return array[0];
}

let result = compose(first, reverse);
console.log(result([1,2,3]))

lodash中的组合函数:flow()、flowRight(),传入的作为参数的函数没有个数限制

const _ = require('lodash');
const toUpper = v => v.toUpperCase();
const first = array => array[0];
const reverse = array => array.reverse();
//flow()是从左到右运行作为参数的函数, flowRight()是从右向左运行作为参数的函数
let result = _.flowRight(toUpper, first, reverse);
console.log(result(['one', 'two', 'three']))

模拟实现lodash中的flowRight()方法

//首先分析一下需求:compose函数的参数为不限制个数的函数(一元函数),返回一个函数用来接收参数函数运行的初始值
//args代表传入的函数的数组,因为是模拟flowRight(),所以要先将args参数数组翻转,然后逐个调用args中的函数,最后返回输出值
//逐个调用作为参数的函数,且前一个的输出作为后一个的输入,因此使用reduce()
function compose(...args){
    return function(value){
        return args.reverse().reduce(function(acc, fn){
            return fn(acc);
        }, value)
    }
}
//es6版本
const compose = (...args) => value => args.reverse().reduce((acc, fn) => fn(acc), value)

调试

组合函数如果出现了问题,该怎么知道是在哪一个函数处出现的bug呢?

我们可以定义一个log()函数,用来打印每次作为参数的函数运行完后的输出值,看符不符合预期

//输出一下前一个函数的输出值,并记得将输出值return一下,作为下一个函数的输入
function log(value){
    console.log(value);
    return value;
}

这样打印出来感觉不是很明确到底是哪一步输出的,可以定义一个trace()函数

function trace(tag, value){
    console.log(tag, vlaue);
    return value;
}
//但是我们的组合函数所需要的参数为一元函数,所以要将trace()柯里化,调用lodash的柯里化方法或者自己写的方法
const _ = require('lodash')
const trace = _.curry((tag, value) => {
    console.log(tag, value);
    return value;
})
//调试,此时的trace()函数是柯里化后的函数,因此可以先传入tag参数,明确是哪一步的输出
let result = compose(toUpper, trace('first后'), first, trace('reverse后'), reverse);

fp模块

在lodash中有一个fp模块,其中的方法有两个特点:

  • 对函数式编程友好
  • 具有auto-curried iteratee-first data-last的特点即自动柯里化,函数优先,数据置后 而lodash中的方法基本上都是数据优先,函数置后
// lodash模块
const _ = require('lodash');

let m = _.map(['a', 'b', 'c', 'd'], _.toUpper); // ['A', 'B', 'C', 'D']

let m = _.split('a-bc-de', '-'); // ['a', 'bc', 'de']
// fp模块
const fp = require('lodash/fp');

let f = fp.map(fp.toUpper, ['a', 'b', 'c', 'd']) // ['A', 'B', 'C', 'D']

// fp模块中的放啊都是经过柯里化的,不完全传入参数,返回的是一个等待剩余参数的函数
let f1 = fp.map(fp.toUpper);
let result = f1(['a', 'b', 'c', 'd']); // ['A', 'B', 'C', 'D']

Point Free

将数据处理过程定义成与数据无关的组合运算,不需要用到代表数据的那个参数,只要把简单的运算步骤合成到一起。在此之前需要定义一些辅助的基本运算函数。

  • 不需要指明处理的数据
  • 只需要合成运算过程
  • 需要定义一些基本的运算函数
// 将Whole World转化为whole_world
// 非Point Free模式
function trans(str){

return str.toLowerCase().replace(/\s+/g, '_')

}

console.log(trans('Whole World'))

// point free模式

let f = fp.flowRight(fp.join('_'), fp.split(/\s+/g), fp.toLower);

console.log(f('Whole World'));