JavaScript的函数式编程探索

本文阅读 5 分钟
首页 知识库 正文

01.Ramda,一款实用的 JavaScript 函数式编程库

工欲善其事必先利其器,JavaScript函数式编程怎能没有库这种利器!

Ramda是一个非常适合函数式编程的函数库。函数库类似的有lodash

lodash已经很成熟了,提供了足够好用的函数,那为什么还要Ramda呢?笔者认为:Ramda的理念更适合函数式编程。

Ramda的理念是:

Function first,data last.

也即函数优先,数据靠后。体现在具体的用法区别上,就是lodash会把第一个参数当成要处理的数据,函数等放在第一个参数之后传进来。而Ramda会把要出来的数据放在最后一个参数,最后一个参数之前的参数,都是传入的函数。

  1. const list = [{a: 1}, {a: 2}, {a: 3}];
  2. //lodash:数据在前
  3. _.findIndex(list, (o)=> o.a== 2); //>> 0
  4. //Ramda:数据在最末尾
  5. R.findIndex(R.propEq('a', 2),list); //>> 1

将数据放在后面有什么好处呢?

好处是写的代码更精简,更方便阅读理解。这点结合Ramda的柯里化会感受更加明显。

02.Ramda的函数都是柯里化的

上面的代码第8行,可以写成:

  1. R.findIndex(R.propEq('a', 2))(list); //>> 1

也是可以的,这不就是柯里化形态吗。没错,而Ramda提供的所有函数都是已经柯里化的,这可以从源代码找到证据。看看最简单的add函数的源代码:

  1. import _curry2 from './internal/_curry2';
  2. /**
  3. * Adds two values.
  4. *
  5. * @func
  6. * @memberOf R
  7. * @since v0.1.0
  8. * @category Math
  9. * @sig Number -> Number -> Number
  10. * @param {Number} a
  11. * @param {Number} b
  12. * @return {Number}
  13. * @see R.subtract
  14. * @example
  15. *
  16. * R.add(2, 3); //=> 5
  17. * R.add(7)(10); //=> 17
  18. */
  19. var add = _curry2(function add(a, b) {
  20. return Number(a) + Number(b);
  21. });
  22. export default add;

上面代码第1行就引入了_curry2这个内部函数,然后第21行调用_curry2add函数柯里化。Ramda每个对外的函数,都这样处理过。因此Ramda对外提供的每个函数都是柯里化过的。

全部柯里化的目的是什么呢?

仍然是为了与前面讲述的“将数据放在末尾”结合使用,以便体现代码的简洁、可读性。

现在举例说明:求列表中a的值大于1的项,然后取各项值之和。

  1. const list = [{ a: 1 }, { a: 2 }, { a: 3 }];
  2. //lodash
  3. {
  4. //根据给定的列表求和
  5. let sum = data => _.reduce(data, (a, b) => a + b.a,0);
  6. //得到值>1的项组成的列表
  7. let getList = data => _.filter(data, (o) => o.a > 1);
  8. let total = _.flow(getList, sum)(list);
  9. console.log(total);//>> 5
  10. }
  11. //Ramda
  12. {
  13. let sum = data => R.reduce((a,b)=>a+b.a,0,data);
  14. let getList = data => R.filter((o) => o.a > 1,data);
  15. let total = R.compose(sum,getList)(list);
  16. console.log(total);//>> 5
  17. }

这样可能还看不出明显差别,别急,因为Ramda提供的函数都是柯里化过的,柯里化可以将多参数函数拆分成单参数函数,于是可以改成如下:

  1. let sum = data => R.reduce((a,b)=>a+b.a,0)(data);
  2. let getList = data => R.filter((o) => o.a > 1)(data);
  3. let total = R.compose(sum,getList)(list);
  4. console.log(total);//>> 5

基本可以发现一个Ramda代码的特征:第1、2行代码,作为数据的参数data固定地出现在参数和最末尾位置。既然第1,2行代码都有传数据参数data,而第3行代码传数据参数list,重复这么多次没有必要,同样是将数据以参数传递,笔者直觉这种情况出现一次就够了(理论上,代码里有重复的地方都可以优化,出现一次就够 )。

那第1、2行多余的参数传递可以优化掉!因为末尾都是数据,那可以十分方便地优化掉(拿走就行了,比将数据放在最开头的参数位置而言,要方便得多)。再因为Ramda提供的函数都是柯里化过的,柯里化可以延迟运行,也即可以暂时不用传数据进来,在真正需要运行函数的时候传数据进来就可以了。所以,代码优化如下:

  1. let sum = R.reduce((a,b)=>a+b.a,0);//优化掉data
  2. let getList = R.filter((o) => o.a > 1);//优化掉data
  3. let total = R.compose(sum,getList)(list);//最后再统一传参data
  4. console.log(total);//>> 5

哇!上面代码中第1、2行原本需要传递的参数data不见了,效果却一样!居然可以没data什么事?!的确可以!很清爽的代码。至此,就引出了一种无参数的编程风格:Pointfree ,放在下一篇单独介绍。

可以发现正是因为Ramda把数据放在参数的最后一个位置,同时每个函数都柯里化过,因此能够省略一些参数,代码才变得更简洁,更易读。

参考文献

The Philosophy of Ramda

解压密码: detechn或detechn.com

免责声明

本站所有资源出自互联网收集整理,本站不参与制作,如果侵犯了您的合法权益,请联系本站我们会及时删除。

本站发布资源来源于互联网,可能存在水印或者引流等信息,请用户自行鉴别,做一个有主见和判断力的用户。

本站资源仅供研究、学习交流之用,若使用商业用途,请购买正版授权,否则产生的一切后果将由下载用户自行承担。

用JavaScript实现接口
« 上一篇 11-16
什么是函数式编程?
下一篇 » 11-16

发表评论

惪特博客
  • 文章总数:
    18502 篇
  • 评论总数:
    53434 条
  • 标签总数:
    8884 个
  • 总浏览量:
    24343335 次
  • 最后更新:
    5月15日

最多点赞

随便看看

标签TAG