一 函数组合

纯函数和柯里化很容易写出洋葱代码 h(g(f(x)));
例:获取数组的最后一个元素再转换成大写字母 .toUpper(.first(_.reverse(array)));
函数组合可以让我们把细粒度的函数重新组合生成一个成新的函数;
compose 如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数;
函数就像数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果
函数组合默认从右到左执行;

  1. function compose(f,g){
  2. return function(value){
  3. return f(g(value))
  4. }
  5. }
  6. function reverse(arr) {
  7. return arr.reverse();
  8. }
  9. function first(arr) {
  10. return arr[0]
  11. };
  12. var last = compose(first, reverse);
  13. console.log(last([1,2,3,4]))

二 lodash中的组合函数 flow flowRight

  1. // lodash 中的组合函数
  2. const _ = require('lodash');
  3. const reverse = arr => arr.reverse();
  4. const first = arr => arr[0];
  5. const toUpper = str => str.toUpperCase();
  6. const f = _.flowRight(toUpper, first, reverse);
  7. console.log(f(['one', 'two', 'three']));
  8. // lodash 中的组合函数
  9. const _ = require('lodash');
  10. const reverse = arr => arr.reverse();
  11. const first = arr => arr[0];
  12. const toUpper = str => str.toUpperCase();
  13. // const f = _.flowRight(toUpper, first, reverse);
  14. const reverse = arr => arr.reverse();
  15. const first = arr => arr[0];
  16. const toUpper = str => str.toUpperCase();
  17. // const f = _.flowRight(toUpper, first, reverse);
  18. // const flowRight = function(...args) {
  19. // return function(value){
  20. // return args.reverse().reduce(function(acc,fn){
  21. // return fn(acc)
  22. // }, value);
  23. // }
  24. // }
  25. const flowRight = function(...args) {
  26. return value => args.reverse().reduce((acc,fn) => fn(acc), value);
  27. }
  28. const f = flowRight(toUpper, first, reverse);
  29. console.log(f(['one', 'two', 'three']));

三 函数组合要满足结合律

  1. // lodash 中的组合函数
  2. const _ = require('lodash');
  3. // const f = _.flowRight(_.toUpper, _.first, _.reverse);
  4. const f = _.flowRight(_.flowRight(_.toUpper, _.first), _.reverse);
  5. // const f = _.flowRight(_.toUpper, _.flowRight(_.first, _.reverse));
  6. console.log(f(['one', 'two', 'three']));

四 lodash中的FP模块

  1. lodash中的FP模块提供了实用的对函数式编程友好的方法;提供了不可变auto-curried iterate-first data-last(柯里化、函数优先、数据在后)的方法
  1. // lodash模块
  2. const _ = require('lodash');
  3. _.map(['a','b','c'], _.toUpper);
  4. _.map(['a','b','c']);
  5. _.split(['hello world'],' ');
  6. // lodash fp模块
  7. const fp = require('lodash/fp');
  8. fp.map(fp.toUpper, ['a','b','c']);
  9. fp.map(fp.toUpper)(['a','b','c']);
  10. fp.split(' ', 'hello world')
  11. fp.split(' ')('hello world')

fp案例

  1. // lodash fp模块
  2. const fp = require('lodash/fp');
  3. const f = fp.flowRight(fp.join('-'), fp.map(fp.toLower), fp.split(' '));
  4. console.log(f('NEVER GIVE UP'))

lodash fp中map

  1. // lodash fp模块
  2. const _ = require('lodash');
  3. console.log(_.map(['22','12','9'],parseInt));
  4. const fp = require('lodash/fp');
  5. console.log(fp.map(parseInt, ['22','12','9']))

练习

  1. const fp = require('loadsh/fp');
  2. const cars = [
  3. {name: 'Ferrari FF', horsepower: 660, dollar_value: 700000, in_stock: true },
  4. {name: 'Spyker c12 Zagato', horsepower: 650, dollar_value: 648000, in_stock: false },
  5. {name: 'Jaguar XKR-S', horsepower: 550, dollar_value: 132000, in_stock: false },
  6. {name: 'Audi R8', horsepower: 525, dollar_value: 114200, in_stock: false },
  7. {name: 'Aston Martin One-77', horsepower: 750, dollar_value: 1850000, in_stock: true },
  8. {name: 'Pagani Huayra', horsepower: 700, dollar_value: 1300000, in_stock: false },
  9. ];
  10. // 使用fp.flowRight重新实现这个函数
  11. let isLastInStock = function (cars) {
  12. let last_car = fp.last(cars);
  13. return fp.prop('in_stock', last_car)
  14. }
  15. console.log('export 1', isLastInStock(cars));
  16. let getLast = fp.flowRight(fp.prop('in_stock'), fp.last);
  17. console.log('ans 1', getLast(cars))
  18. // 使用fp的flowright、prop、first获取第一个car的name
  19. let getFirstName = fp.flowRight(fp.prop('name'), fp.first);
  20. console.log('ans 2', getFirstName(cars));
  21. // 使用帮助函数_average重构averageDollarValue,使用函数组合的方式实现
  22. let _average = function (xs) {
  23. return fp.reduce(fp.add, 0, xs)/xs.length;
  24. }
  25. let averageDollarValue = function(cars) {
  26. let dollar_values = fp.map(function(car){
  27. return car.dollar_value
  28. }, cars);
  29. return _average(dollar_values);
  30. }
  31. console.log('export 3', averageDollarValue(cars));
  32. let fpAverage = fp.flowRight(_average, fp.map( car => car.dollar_value));
  33. console.log('ans 3', fpAverage(cars));
  34. // 使用flowright写一个sanitizeNames()函数,返回一个下划线连接的小写字符串,把数组中的name转换成这种形式
  35. // sanitizeNames(['Hello World']) => ["hello_world"]
  36. let _underscore = fp.replace(/\W+/g, '_');
  37. let fpSan = fp.flowRight(_underscore, fp.toLower, fp.prop('name'));
  38. let fpSanArr = fp.flowRight(fp.map( item => fpSan(item)))
  39. console.log(fpSanArr(cars))

**

五 Point Free 一种编程风格

把数据处理的过程定义成与数据无关的合成运算,在当中不需要用到代表数据的参数,只要把运算步骤合到一起;使用这种模式之前,需要定义一些辅助的基本运算函数;
不需要指明处理的数据,只需要合成运算过程,需要定义一些辅助的运算函数;