函数组合 (compose) - 图1

1. 概念

  1. 纯函数和柯里化很容易写出洋葱代码 h(g(f(x))) ,函数组合可以让我们把细粒度的函数重新组合生成一个新的函数
  2. 如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数
  3. 函数就像是数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果
    1. (当我们遇到函数比较复杂时,可以把函数拆分成很多的小函数,此时多了中间过程产生的参数)

image.png

  1. 函数组合默认是从右到左执行
  2. 案例 ```javascript // 函数组合演示 // 获取数组最后一个值

// 把函数进行组合 注意的是 函数组合默认是从右到左执行 function compose(f, g) { return function (value) { return f(g(value)) } } // 反转 function reverse(array){ return array.reverse(); } // 取第一个元素 function first(array){ return array[0]; }

const last = compose(first,reverse); console.log(last([“a”,”b”,”c”,”d”]));

  1. <a name="SUUgx"></a>
  2. ## 2. lodash中的组合函数
  3. <a name="T7uHI"></a>
  4. #### 1.lodash 中组合函数 flow() 或者 flowRight(),他们都可以组合多个函数
  5. flow() 是从左到右运行<br />flowRight() 是从右到左运行,使用的更多一些<br />案例:
  6. ```javascript
  7. // lodash 中的组合函数 flowRight() 是从右到左运行
  8. const _ = require('lodash')
  9. // 翻转
  10. const reverse = arr => arr.reverse()
  11. // 求第一个元素
  12. const first = arr => arr[0]
  13. // 转大写
  14. const toUpper = str => str.toUpperCase()
  15. // 使用lodash中的flowRight 来进行组合函数
  16. const fn = _.flowRight(toUpper,first,reverse);
  17. console.log(fn(['one','tow','three']));

2.lodash_fp 模块

  1. 提供了实用的对函数式编程友好的方法
  2. 提供了不可变 auto-curried, iteratee-first, data-last 的方法
  3. fp中提供的方法都是柯里化的 如果有多个参数都是函数优先,数据滞后的
  4. 使用fp模块的示例 ```javascript // MEVER SAY DIE —-》nerver-say-die // fp中提供的方法都是柯里化的 如果有多个参数都是函数优先数据滞后的 const fp = require(‘lodash/fp’);

const fn = fp.flowRight(fp.join(‘-‘), fp.map(fp.toLower), fp.split(‘ ‘))

console.log(fn(‘MEVER SAY DIE’));

  1. 1. lodashlodsh/fp模块中 map方法的区别
  2. ```javascript
  3. // lodash和lodsh/fp模块中 map方法的区别
  4. // lodash
  5. // const _ = require('lodash');
  6. // console.log(_.map(['23','8','10'],parseInt));
  7. // // parseInt('23',0,array)
  8. // // parseInt('8',1,array)
  9. // // parseInt('10',2,array)
  10. // //parseInt 的第二个参数其实是 转化为几进制
  11. // // 23 0其实是10进制
  12. // // 8 1进制没有 所以等于NaN
  13. // // 10 二进制结果为2
  14. // // 所以 结果为[ 23, NaN, 2 ]
  15. // end
  16. // lodsh/fp
  17. const fp = require('lodash/fp');
  18. console.log(fp.map(parseInt,['23','8','10']));
  19. // end

3. 组合函数实现原理

  1. // 模拟 lodash中的 组合函数
  2. // 翻转
  3. const reverse = arr => arr.reverse()
  4. // 求第一个元素
  5. const first = arr => arr[0]
  6. // 转大写
  7. const toUpper = str => str.toUpperCase()
  8. // 模拟 lodash中的 flowRight 组合函数(从右到左运行)
  9. function compose(...args) {
  10. return function (value) {
  11. /**
  12. * reduce 为数组中的每一个元素依次执行回调函数
  13. * reduce() 方法接收一个函数作为累加器,
  14. * 数组中的每个值(从左到右)开始缩减,最终计算为一个值。
  15. * reduce() 可以作为一个高阶函数,用于函数的 compose。
  16. * 注意: reduce() 对于空数组是不会执行回调函数的。
  17. * reduce(函数(total:必需。初始值, 或者计算结束后的返回值
  18. * currentValue:必需。当前元素
  19. * currentIndex:可选。当前元素的索引
  20. * arr:可选。当前元素所属的数组对象。)
  21. * ,初始值(可选))
  22. */
  23. return args.reverse().reduce(function(acc,fn){
  24. return fn(acc);
  25. },value)
  26. }
  27. }
  28. // ES6 箭头函数表示
  29. // const compose = (...args) => value => args.reverse().reduce((acc,fn)=>fn(acc),value)
  30. // 使用模拟的组合函数 来进行组合函数
  31. const fn = compose(toUpper, first, reverse);
  32. console.log(fn(['one', 'tow', 'three','aaaaa']));

4. 结合律(函数组合要满足的一个特点)

(a×b)×c=a×(b×c)
我们可以先把a和b组合 也可以先把b和c组合
结果是一样的
案例:

  1. // lodash 中的组合函数 flowRight() 是从右到左运行
  2. const _ = require('lodash')
  3. // 使用lodash中的flowRight 来进行组合函数
  4. const fn1 = _.flowRight(_.toUpper,_.first,_.reverse);
  5. const fn2 = _.flowRight(_.flowRight(_.toUpper,_.first),_.reverse);
  6. const fn3 = _.flowRight(_.toUpper,_.flowRight(_.first,_.reverse));
  7. console.log(fn1(['one','tow','three']));
  8. console.log(fn2(['one','tow','three']));
  9. console.log(fn3(['one','tow','three']));

5. 调试

  1. // 函数组合调试
  2. // MEVER SAY DIE ---》nerver-say-die
  3. const _ = require('lodash');
  4. // 调试函数 打印然后把拿到的值再输出出去(打印会不清晰没有标记tag)
  5. const log = v => {
  6. console.log(v);
  7. return v;
  8. }
  9. const trace = _.curry((tag, v) => {
  10. console.log(tag, v);
  11. return v
  12. })
  13. // 切割字符串柯里化
  14. const split = _.curry((sep, str) => _.split(str, sep));
  15. // 拼接柯里化
  16. const join = _.curry((sep, array) => _.join(array, sep));
  17. // map 对数组中的每个元素进行处理 柯里化
  18. const map = _.curry((fn, arr) => _.map(arr, fn));
  19. const fn = _.flowRight(join('-'), trace('map后'), map(_.toLower), trace('split后'), split(' '))
  20. console.log(fn('MEVER SAY DIE'));

我们可以自己写一个调试函数trace 利用 函数组合中 上一个函数返回值传递给下一个参数 我们就可以从中获取到这个参数 从而进行调试

6. PointFree

概念

  1. 一种编程的风格,具体实现是函数的组合,他更抽象一些
  2. 我们可以把数据处理的过程定义成与数据无关的合成运算,不需要用到代表数据的那个参数,只要把简单的运算步骤合成到一起,在使用这种模式之前我们需要定义一些辅助的基本运算函数。
    1. 不需要指明处理的数据
    2. 只需要合成运算过程
    3. 需要定义一些辅助的基本运算函数

      案例

      ```javascript // point free // Hello world => hello_world

const fp = require(‘lodash/fp’) // replace字符串中用一些字符替换另一些字符 const f = fp.flowRight(fp.replace(/\s+/g, ‘_’), fp.toLower);

console.log(f(‘Hello world’));

  1. ```javascript
  2. // point free
  3. // world wild web => W. W. W
  4. const fp = require('lodash/fp')
  5. // replace字符串中用一些字符替换另一些字符
  6. // const firstLetterToUpper = fp.flowRight(fp.join('. '),fp.map(fp.first),fp.map(fp.toUpper),fp.split(' '))
  7. const firstLetterToUpper = fp.flowRight(fp.join('. '),fp.map(fp.flowRight(fp.toUpper,fp.first)),fp.split(' '))
  8. console.log(firstLetterToUpper('world wild web'));