函数式编程 - 组合compose的使用方法

函数式编程中有一个比较重要的概念就是函数组合(compose),组合多个函数,同时返回一个新的函数。调用时,组合函数按顺序从右向左执行。右边函数调用后,返回的结果,作为左边函数的参数传入,严格保证了执行顺序,这也是compose 主要特色。| 函数式编程--函数组合(Function composition) - 知乎

入门简介


组合两个函数

compose 非常简单,通过下面的示例代码,就非常清楚。

function compose (f, g) {return function(x) {return f(g(x));}
}var arr = [1, 2, 3],
reverse = function(x){ return x.reverse()},
getFirst = function(x) {return x[0]},
compseFunc = compose(getFirst, reverse);compseFunc(arr);   // 3

参数在函数间就好像经过‘管道’传输同样,最右边的函数接收外界参数,返回结果传给左边的函数,最后输出结果。

组合任意个函数

上面组合了两个函数的compose,也让咱们了解了组合的特点,接着咱们看看如何组合更多的函数,由于在实际应用中,不会像入门介绍的代码那么简单。

主要注意几个关键点:

1、利用arguments的长度得到所有组合函数的个数

2、reduce 遍历执行全部函数。

var compose = function() {var args = Array.prototype.slice.call(arguments);return function(x) {if (args.length >= 2) {return args.reverse().reduce((p, c) => {return p = c(p)}, x)} else {return args[1] && args[1](x);}}}// 利用上面示例 测试一下。
var arr = [1, 2, 3],
reverse = function(x){ return x.reverse()},
getFirst = function(x) {return x[0]},
trace = function(x) {  console.log('执行结果:', x); return x}compseFunc = compose(trace, getFirst, trace, reverse);compseFunc(arr);   // 执行结果: (3) [3, 2, 1]// 执行结果: 3// 3

如此实现,基本没什么问题,变量arr 在管道中传入后,经过各种操作,最后返回了结果。

深入理解


认识pipe

函数式编程(FP)里面跟compose类似的方法,就是pipe

pipe,主要作用也是组合多个函数,称之为, 肯定得按照正常方法,从左往右调用函数,与compose 调用方法相反。

ES6 实现Compose function

先看下compose 最基础的两参数版本

const compose = (f1, f2) => value => f1(f2(value));

利用箭头函数,非常直接的表明两个函数嵌套执行的关系,接着看多层嵌套。

(f1, f2, f3...) => value => f1(f2(f3));

抽象出来表示:

() => () => result;

先提出这些基础的组合方式,对我们后面理解高级ES6方法实现compose有很大帮助。

实现pipe

前面提到 pipe 是反向的compose,pipe正向调用也致使它实现起来更容易。

pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)

一行代码就实现了pipe,套用上面抽象出来的表达式,reduce恰好正向遍历全部函数, 参数x做为传递给函数的初始值, 后面每次f(v)执行的结果,作为下一次f(v)调用的参数v,完成了函数组合调用。

或者,能够把函数组合中,第一个函数获取参数后,获得的结果,作为reduce遍历的初始值。

pipe = (fn,...fns) => (x) => fns.reduce( (v, f) => f(v), fn(x));

利用ES6提供的rest 参数 ,用于获取函数的多余参数,提取出第一个函数fn,多余函数参数放到fns中,fns看成是数组,也不用像arguments那种事先经过Array.prototype.slice.call转为数组,arguments对性能损耗也可以避免。 fn(x) 第一个函数执行结果做为reduce 初始值。

注:关于剩余参数rest,可参考我之前文章:【JS高级】ES6参数增强之剩余参数的应用

实现compose

1、pipe 部分,利用reduce实现,反过来看,compose就能够利用reduceRight

compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

2、利用递归

compose = (fn, ...fns) => fns.length === 0 ? fn: (...args) => fn(compose(...fns)(...args))

递归代码,首先看出口条件, fns.length === 0,最后一定执行最左边的函数,然后把剩下的函数再经过compose调用,

3、利用reduce实现。

一行实现,并且仍是用正向的 reduce

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))

作者其实用例子作了解释,可以看下reduce 迭代的方向是从左往右的,而compose 要求执行的方向是从右往左。对数组中每一项执行函数,正常状况下都应该放回执行结果,比如(v, f) => f(v),返回f(v)执行结果,这里是(f, g) => (...args) => f(g(...args))返回一个函数(...args) => f(g(...args)),这样就能够保证后面的函数g在被作为参数传入时比前面的函数f先执行。

简单利用前面的组合两个函数的例子分析一下。

...
composeFunc = compose(getFirst, trace, reverse);
composeFunc(arr);

主要看reduce 函数里面的执行过程:

◆ 入口 composeFunc(arr), 第一次迭代,reduce函数执行 (getFirst, trace) => (...args)=>getFirst(trace(...args)),函数(...args)=>getFirst(trace(...args))作为下一次迭代中累计器f的值。

◆ 第二次迭代,reduce函数中

f == (...args)=>getFirst(trace(...args))
g == reverse。
// 替换一下 (f, g) => (...args) => f(g(...args))
((...args)=>getFirst(trace(...args)), reverse) => (...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))

◆ 迭代结束,最后得到的comoseFunc就是

// 对照第二次的执行结果, (...args) => f(g(...args))(...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))

◆ 调用函数composeFunc(arr)。

(arr) => ((...args)=>getFirst(trace(...args)))(reverse(arr))===》reverse(arr) 执行结果[3, 2, 1] 做为参数((...args)=>getFirst(trace(...args)))([3,2,1])==》入参调用函数getFirst(trace[3,2,1])===》 getFirst([3, 2, 1])===》结果为 3

非常巧妙的把后一个函数的执行结果作为包裹着前面函数的空函数的参数,传入执行。其中大量用到下面的结构。

((arg)=> f(arg))(arg) 
// 转换一下。(function(x) {return f(x)})(x)

最后

不管是compose, 仍是后面提到的pipe,概念很是简单,均可以使用很是巧妙的方式实现(大部分使用reduce),并且在编程中很大程度上简化代码。最后列出优秀框架中使用compose的示例:

  • redux/compose
  • koa-Compose
  • underscorejs/compose

参考连接

  • Creating an ES6ish Compose in Javascript
  • compose.js
  • Optimization-killers

参考资料:

JS高级编程中compose函数的介绍和基本实现 |  复合函数compose函数的概念

compose函数 | 函数式编程--函数组合(Function composition) | 函数式编程之compose

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/344371.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Netty开篇——NIO章上(三)

Java NIO基本介绍 java non-blocking I/O 称为NIO(也叫New IO)。JDK4开始提供,同步非阻塞相关内容在 java.nio 包及子包下,对java.io 包中的很多类进行改写。三大核心: Channel(通道),Buffer(缓冲区),Selector(选择器)NIO是面向缓冲区或者面向块编程的。…

最佳实践:如何在 SoapUI 中进行 REST 服务的测试

SoapUI 频繁地被选择为 SOAP 和 REST API 的自动化测试利器,得益于其友好的用户界面,测试人员毫不费力便可校验 REST 和 SOAP 协议的 Web 服务。它尤其适用于那些需要进行复杂测试的场合。 1、设置接口 我利用了 Swagger 去设置了一批即将投入使用的接…

蓝桥杯省赛无忧 STL 课件13 list

01 list的定义和结构 以下是一个示例&#xff0c;展示如何使用listt容器: #include<bits/stdc.h> using namespace std; int main(){list<int> mylist;//在链表尾部插入元素mylist.push_back(1);mylist.push_back(2);mylist.push_back(3);//在链表头部插入元素 my…

第二证券:A股指数反弹 飞行汽车概念骤然升温

1月11日&#xff0c;A股三大指数集体反弹&#xff0c;成交量温文放大&#xff0c;北向资金午后跑步出场。盘面上&#xff0c;工作板块个股出现普涨态势。翱翔轿车、鸿蒙概念股全天走强&#xff1b;午后新能源龙头放量拉升&#xff0c;带动创业板指快速走高。 到收盘&#xff0…

典型场景解析|PolarDB分布式版如何支撑SaaS多租户?

SaaS多租户背景 很多平台类应用或系统&#xff08;如电商CRM平台、仓库订单平台等等&#xff09;&#xff0c;它们的服务模型是围绕用户维度&#xff08;这里的用户维度可以是一个卖家或品牌&#xff0c;可以是一个仓库等&#xff09;展开的。因此&#xff0c;这类型的平台业务…

.nfsxxxxxx文件无法删除

先&#xff1a; sudo apt-get update sudo apt-get install lsof然后&#xff1a; lsof 文件路径 输出&#xff1a; 37012是id号 kill -9 id号 参考文章&#xff1a; 如何删除服务器出现的.nfs文件-CSDN博客 如何删除.nfs00000xxxx文件_.nfs0000000000004cca0000002a-CSDN博…

助力工业园区作业违规行为检测预警,基于YOLOv8【n/s/m/l/x】全系列参数模型开发构建工业园区场景下作业人员违规行为检测识别系统

在很多工业园区生产作业场景下保障合规合法进行作业生产操作&#xff0c;对于保护工人生命安全降低安全隐患有着非常重要的作用&#xff0c;但是往往在实际的作业生产中&#xff0c;因为一个安全观念的淡薄或者是粗心大意&#xff0c;对于纪律约束等意思薄弱&#xff0c;导致在…

多国管理中心多语言区块链源码一元夺宝程序仿趣步奕跑/原生计步器/原生人脸识别

前后台分开的&#xff0c;后台是TP3.2的框架了。 目前把整体UI 改版黄色系风格&#xff0c;集成了一元夺宝程序&#xff0c;用户数据同步趣步&#xff0c;效果看起来很棒&#xff0c;另外加入股票走势图&#xff08;K线图&#xff09;&#xff0c;目前已经继承人脸识别&#xf…

美国安规测试UL 60335-2-3 安全标准家用和类似用途电器安全第 2-3 部分:电熨斗的特殊要求

UL 60335-2-3 安全标准家用和类似用途电器安全第 2-3 部分:电熨斗的特殊要求 本 UL 标准基于 IEC 出版物 60335-2-3:6.1 版&#xff0c;家用和类似用途电器-安全-第 2-3 部分:电熨斗的特殊要 求。 IEC 出版物 60335-2-3 版权归 IEC 所有。 本版本的发布是为了满足 UL 标准政…

掌握 Vue 响应式系统,让数据驱动视图(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

HarmonyOS应用开发学习笔记 UIAbility组件与UI的数据同步 EventHub、globalThis

1、 HarmoryOS Ability页面的生命周期 2、 Component自定义组件 3、HarmonyOS 应用开发学习笔记 ets组件生命周期 4、HarmonyOS 应用开发学习笔记 ets组件样式定义 Styles装饰器&#xff1a;定义组件重用样式 Extend装饰器&#xff1a;定义扩展组件样式 5、HarmonyOS 应用开发…

JVM基础(8)——CMS垃圾回收器

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…