一 函数组合
纯函数和柯里化很容易写出洋葱代码 h(g(f(x)));
例:获取数组的最后一个元素再转换成大写字母 .toUpper(.first(_.reverse(array)));
函数组合可以让我们把细粒度的函数重新组合生成一个成新的函数;
compose 如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数;
函数就像数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果
函数组合默认从右到左执行;
function compose(f,g){return function(value){return f(g(value))}}function reverse(arr) {return arr.reverse();}function first(arr) {return arr[0]};var last = compose(first, reverse);console.log(last([1,2,3,4]))
二 lodash中的组合函数 flow flowRight
// lodash 中的组合函数const _ = require('lodash');const reverse = arr => arr.reverse();const first = arr => arr[0];const toUpper = str => str.toUpperCase();const f = _.flowRight(toUpper, first, reverse);console.log(f(['one', 'two', 'three']));// lodash 中的组合函数const _ = require('lodash');const reverse = arr => arr.reverse();const first = arr => arr[0];const toUpper = str => str.toUpperCase();// const f = _.flowRight(toUpper, first, reverse);const reverse = arr => arr.reverse();const first = arr => arr[0];const toUpper = str => str.toUpperCase();// const f = _.flowRight(toUpper, first, reverse);// const flowRight = function(...args) {// return function(value){// return args.reverse().reduce(function(acc,fn){// return fn(acc)// }, value);// }// }const flowRight = function(...args) {return value => args.reverse().reduce((acc,fn) => fn(acc), value);}const f = flowRight(toUpper, first, reverse);console.log(f(['one', 'two', 'three']));
三 函数组合要满足结合律
// lodash 中的组合函数const _ = require('lodash');// const f = _.flowRight(_.toUpper, _.first, _.reverse);const f = _.flowRight(_.flowRight(_.toUpper, _.first), _.reverse);// const f = _.flowRight(_.toUpper, _.flowRight(_.first, _.reverse));console.log(f(['one', 'two', 'three']));
四 lodash中的FP模块
lodash中的FP模块提供了实用的对函数式编程友好的方法;提供了不可变auto-curried iterate-first data-last(柯里化、函数优先、数据在后)的方法
// lodash模块const _ = require('lodash');_.map(['a','b','c'], _.toUpper);_.map(['a','b','c']);_.split(['hello world'],' ');// lodash fp模块const fp = require('lodash/fp');fp.map(fp.toUpper, ['a','b','c']);fp.map(fp.toUpper)(['a','b','c']);fp.split(' ', 'hello world')fp.split(' ')('hello world')
fp案例
// lodash fp模块const fp = require('lodash/fp');const f = fp.flowRight(fp.join('-'), fp.map(fp.toLower), fp.split(' '));console.log(f('NEVER GIVE UP'))
lodash fp中map
// lodash fp模块const _ = require('lodash');console.log(_.map(['22','12','9'],parseInt));const fp = require('lodash/fp');console.log(fp.map(parseInt, ['22','12','9']))
练习
const fp = require('loadsh/fp');const cars = [{name: 'Ferrari FF', horsepower: 660, dollar_value: 700000, in_stock: true },{name: 'Spyker c12 Zagato', horsepower: 650, dollar_value: 648000, in_stock: false },{name: 'Jaguar XKR-S', horsepower: 550, dollar_value: 132000, in_stock: false },{name: 'Audi R8', horsepower: 525, dollar_value: 114200, in_stock: false },{name: 'Aston Martin One-77', horsepower: 750, dollar_value: 1850000, in_stock: true },{name: 'Pagani Huayra', horsepower: 700, dollar_value: 1300000, in_stock: false },];// 使用fp.flowRight重新实现这个函数let isLastInStock = function (cars) {let last_car = fp.last(cars);return fp.prop('in_stock', last_car)}console.log('export 1', isLastInStock(cars));let getLast = fp.flowRight(fp.prop('in_stock'), fp.last);console.log('ans 1', getLast(cars))// 使用fp的flowright、prop、first获取第一个car的namelet getFirstName = fp.flowRight(fp.prop('name'), fp.first);console.log('ans 2', getFirstName(cars));// 使用帮助函数_average重构averageDollarValue,使用函数组合的方式实现let _average = function (xs) {return fp.reduce(fp.add, 0, xs)/xs.length;}let averageDollarValue = function(cars) {let dollar_values = fp.map(function(car){return car.dollar_value}, cars);return _average(dollar_values);}console.log('export 3', averageDollarValue(cars));let fpAverage = fp.flowRight(_average, fp.map( car => car.dollar_value));console.log('ans 3', fpAverage(cars));// 使用flowright写一个sanitizeNames()函数,返回一个下划线连接的小写字符串,把数组中的name转换成这种形式// sanitizeNames(['Hello World']) => ["hello_world"]let _underscore = fp.replace(/\W+/g, '_');let fpSan = fp.flowRight(_underscore, fp.toLower, fp.prop('name'));let fpSanArr = fp.flowRight(fp.map( item => fpSan(item)))console.log(fpSanArr(cars))
五 Point Free 一种编程风格
把数据处理的过程定义成与数据无关的合成运算,在当中不需要用到代表数据的参数,只要把运算步骤合到一起;使用这种模式之前,需要定义一些辅助的基本运算函数;
不需要指明处理的数据,只需要合成运算过程,需要定义一些辅助的运算函数;
