函数式编程 -- 函数组合
文章内容输出来源:拉勾教育 大前端高薪训练营
前言
在学习函数组合之前,我们需要先了解管道的概念。管道在不同的领域,有不同的定义。那么在开发当中,管道是什么呢?
一、管道
当在程序中使用函数处理数据的时候,我们可以把所使用的函数看成是一个管道,通过函数输入对应的参数 ,可以返回相应的结果。
如图所示:
一个程序的运行当中,管道的数量是不固定的,有的时候甚至可以把一个大的管道分割成多个小的管道,然后通过组合的方式,得到程序运行后的结果。
如图所示:
简单来说,管道经常用来将某个命令或程序的输出提供给另一个命令或程序。
代码如下(示例):
fn = compose(f1, f2, f3)b = fn(a)
二、函数组合(compose)
说完了日常的管道概念,下面我们来说一下函数组合。
1. 什么是函数组合
- 如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数。也就是说,将需要嵌套执行的函数进行平铺。嵌套执行指的是,一个函数的返回值将作为另一个函数的参数。
- 函数就像是数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果。
- 函数组合默认是从右到左执行。
2. 模拟函数组合的实现过程
一个简单的例子:
代码如下(示例):
// 函数组合演示 function compose (f, g) { // f, g 都是函数,并且都作为参数进行传入return function (value) { // value 为输入的值return f(g(value)) // 先执行g函数,再执行f函数,符合函数组合默认从右到左执行} } // 箭头函数, 二选一, 此种方式更简便 // let compose = (f, g) => value => f(g(value))let reverse = arr => arr.reverse() let first = arr => return array[0]// 先把数组进行反转,再进行取值 const last = compose(first, reverse) /** -- 程序运行过程* last = (first, reverse) => value => first(reverse(value))*/console.log(last([1, 2, 3, 4])); // 4
3. lodash 中的组合函数
lodash 中组合函数 flow() 或者 flowRight(),他们都可以组合多个函数。
- flow() 是从左到右运行。
- flowRight() 是从右到左运行,使用的更多一些。
下面我们就来模拟一下flowRight()的实现过程:
代码如下(示例):
const _ = require('lodash');const reverse = arr => arr.reverse() const first = arr => arr[0] const toUpper = s => s.toUpperCase()// 多函数组合 function compose(...args) { // 需要接收的参数不确定,使用剩余参数写法return function (value) { // 需要接收参数// 因为需要从右往左执行,所以需要对数组进行反转return args.reverse().reduce(function (acc, fn) {return fn(acc)}, value)} }// 箭头函数写法 const compose = (...args) => (value) => args.reverse().reduce((acc, fn) => fn(acc), value) /** -- 内部函数和reduce()执行过程* args.reverse() = [reverse, first, toUpper]* 第一次迭代:acc = reverse, fn = first* 第二次迭代:acc = first(reverse), fn = toUpper* 第三次迭代:acc = toUpper(first(reverse)), 没有下一项* 因此 迭代结束,返回 toUpper(first(reverse))* * ==> 最终结果* const f = function compose (toUpper ,first , reverse) {* return function (value) {* return toUpper(first(reverse))* }* }* ==>* const = f = compose = (toUpper ,first , reverse) => value => toUpper(first(reverse))*/ const f = compose(toUpper ,first , reverse)console.log(f(['one', 'two', 'three'])) // THREE
reduce()具体用法,详情参见 数组(Array)的常用方法
函数的组合要满足结合律 (associativity):
我们既可以把 g 和 h 组合,还可以把 f 和 g 组合,结果都是一样的。代码如下(示例):
// 结合律(associativity) 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'])) // => THREE , 以上三种方式的结果相同
4. 调试
如何调试组合函数:
通过辅助函数记录日志,传入的参数为上一个函数的执行结果,然后观察每次返回的结果。代码如下(示例):
// NEVER SAY DIE --> never-say-die const _ = require('lodash')// tag 用作标记,v 表示实际传入的上一个函数的执行结果 const trace = _.curry((tag, v) => {console.log(tag, v)return v })// 利用lodash中的curry()方法,将多个函数转换成单一函数,柯里化 const split = _.curry((sep, str) => _.split(str, sep)) const join = _.curry((sep, array) => _.join(array, sep))// 对数组中的每一个元素进行处理,fn 决定如何去处理数组的每一项 const map = _.curry((fn, array) => _.map(array, fn))// 会将split的结果返回给前一个函数 const f = _.flowRight(join('-'), trace('map之后'), map(_.toLower), trace('split之后'), split(' '))console.log(f("NEVER SAY DIE")) // never-say-die
5. lodash中的fp模块
在lodash中文网,关于它里面的fp模块介绍,有这样一句话:
Load the FP build for immutable auto-curried iteratee-first data-last methods
简单来说,就是lodash中的fp模块提供了不可变的三个特性,即:
auto-curried(自动柯里化)
iteratee-first(函数优先)
data-last(数据滞后)
代码如下(示例):
// lodash 中的 fp 模块 const fp = require('lodash/fp');const f = fp.flowRight(fp.join('-'), fp.map(fp.toLower), fp.split(' ')) console.log(f("NEVER SAY DIE"));
lodash中方法 和 fp模块中方法 的不同
1、lodash中方法: 未柯里化,数据优先,函数滞后
2、fp模块中方法:已经被柯里化,函数优先,数据滞后下面我们通过代码的形式,lodash 和 lodash/fp 模块中 map 方法的区别:
代码如下(示例):
// const _ = require('lodash');// console.log(_.map(['23', '8', '10'], parseInt)) // // parseInt('23', 0, array) // // parseInt('8', 1, array) // // parseInt('10', 2, array)const fp = require('lodash/fp')// 区别:接收参数不同 console.log(fp.map(parseInt, ['23', '8', '10']))。
6. Point Free
Point Free:我们可以把数据处理的过程定义成与数据无关的合成运算,不需要用到代表数据的那个参数,只要把简单的运算步骤合成到一起,在使用这种模式之前我们需要定义一些辅助的基本运算函数。
不需要指明处理的数据
只需要合成运算过程
需要定义一些辅助的基本运算函数
案例演示:
// 非 Point Free 模式 // Hello World => hello_world function f (word) { return word.toLowerCase().replace(/\s+/g, '_'); }// Point Free const fp = require('lodash/fp') const f = fp.flowRight(fp.replace(/\s+/g, '_'), fp.toLower) console.log(f('Hello World'))
使用 Point Free 的模式,把单词中的首字母提取并转换成大写
代码如下(示例):
const fp = require('lodash/fp') const firstLetterToUpper = fp.flowRight(join('. '), fp.map(fp.flowRight(fp.first, fp.toUpper)), split(' ')) console.log(firstLetterToUpper('world wild web')) // => W. W. W
函数式编程 -- 函数组合相关推荐
- 翻译连载 | 附录 C:函数式编程函数库-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇...
为什么80%的码农都做不了架构师?>>> 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS> ...
- [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念...
本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程? java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的函数 ...
- java中函数是什么_[一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念...
本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程? java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的函数 看 ...
- 函数式编程 -- 函数是一等公民、高阶函数、闭包
文章内容输出来源:拉勾教育 大前端高薪训练营 前言 学习函数式编程,首先要了解函数式编程相关的概念. 一.函数是一等公民 1. 一等公民的定义 根据维基百科,编程语言中一等公民的概念是由英国计算机学家 ...
- 函数式编程中的组合子
函数式编程是一个比较大的话题,里面的知识体系非常的丰富,在这里我并不想讲的特别的详细.为了应对实际中的应用,我们讲一下函数式编程中最为实用的应用方式--组合子.组合子本身是一种高阶函数,他的特点就是将 ...
- 过程或函数的副作用是_Python函数和函数式编程(两万字长文警告!一文彻底搞定函数,建议收藏!)...
Python函数和函数式编程 函数是可重用的程序代码段,在Python中有常用的内置函数,例如len().sum()等. 在Pyhon模块和程序中也可以自定义函数.使用函数可以提高编程效率. 1.函数 ...
- 【数据分析R语言系列】R语言函数与函数式编程、作用域和apply 家族
文章目录 函数与函数式编程 创建和使用函数 作用域 任意参数 函数式编程 传入和返回函数 apply 家族 apply lapply.sapply 和 vapply 函数与函数式编程 函数是代码模板. ...
- Python进阶:函数式编程(高阶函数,map,reduce,filter,sorted,返回函数,匿名函数,偏函数)...啊啊啊...
函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计 ...
- python中使用函数的优点是什么_python函数式编程是什么?
在以下的文章之中我们来了解一下什么是python中的函数式编程.了解一下python中函数式编程是什么意思,以及python编程函数能应用在什么地方. 函数式编程 函数是Python内建支持的一种封装 ...
最新文章
- ECMAScript6入门--Class对象
- python从入门到精通书-100G Python从入门到精通全套资料!
- Linux arp相关命令(地址解析协议)
- 基于SAML2.0的SAP云产品Identity Authentication过程介绍
- shu函数php,【函数分享】每日PHP函数分享(2021-3-3)
- VUE - get 、post 请求后端接口:get 、post 写法 (Axios 中文说明文档地址)
- 再谈用MFC实现文件拖放到编辑框
- 使用 Hyper-v 虚拟化域控制器
- 《你不知道的JavaScript》-- 精读(一)
- 12-17 学习记录
- 大数据安全分析的特征有哪些
- XMLHttpRequest的方法
- ubuntu下安装win7虚拟机总结
- 20191009每日一句
- CBR,VBR,ABR介绍
- python pipe_Python os.pipe()用法及代码示例
- 计算机网络教室财产登记表,固定资产清查登记表-资产管理.DOC
- 工程材料学习3——第二章 金属材料组织和性能的控制(2.1 纯金属的结晶 2.2 合金的结晶)
- Xshell7工具下载安装以及简单使用
- 只用200行Go代码写一个自己的区块链!