一 函数组合
纯函数和柯里化很容易写出洋葱代码 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的name
let 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 一种编程风格
把数据处理的过程定义成与数据无关的合成运算,在当中不需要用到代表数据的参数,只要把运算步骤合到一起;使用这种模式之前,需要定义一些辅助的基本运算函数;
不需要指明处理的数据,只需要合成运算过程,需要定义一些辅助的运算函数;