javascript柯里化及组合函数~
大家是不是看我上篇博文有点蒙。用的的curry和compose是什么鬼,怎么那么神奇。上篇博文也是主要用到了这两个函数。
那今天我们来聊一下curry和compose,这两个东西是函数式编程很重要的东西,基本每个稍微复杂点的例子都要涉及这两个函数。
什么是curry呢?
---函数柯里化。就是这个东西了。举一个简单的例子。
var _console=curry(function(x){
console.log(x);
})
_console("hello"); //hello
其实就这个作用,先定义函数,后传参数。_console保存了这个函数,每次调用这个函数的时候传给它参数。是不是很简单呢?
看一下curry的实现。
function curry(fn){
return function(f){
return fn(f);
}
}
只是返回一个函数那么简单,这里还涉及fn的控制权反转,类似redux的dispatch,传入dispatch使用它。
那么当这个curry函数有2个参数的时候,curry就得变为:
function curry(fn){
return function(f){
return function(g){
return fn(f,g);
}
}
}
那么有3个参数呢?
function curry(fn){
return function(f){
return function(g){
return function(h){
return fn(f,g,h);
}
}
}
}
那么当我想一次传2个参数,剩下一个传1个参数怎么办?
function curry(fn){
return function(f){
return function(g,h){
return fn(f,g,h);
}
}
}
那么我改主意了,我不确定每次传多少个参数了,我不确定一共传多少个参数了。怎么办?
自动柯里化函数,你会发现_.curry完美的解决了这个问题,甚至可以在你传一个空参数的时候忽略它。
今天我们不研究_.curry的实现,有大神可以分享一下~
我们今天写一个自动柯里化函数。
怎么写?什么思路呢?它的难点在哪?
首先我们需要知道自动柯里化函数的执行过程和普通柯里化函数的执行过程是一样的,即有多少参数返回多少的function,取到函数的参数的长我们知道,
但是函数又不是数组,可以用一层for循环return出来,只能改变思路,函数的循环就是递归了。
它的难点在哪?
我们需要在每次执行一次()的时候获取他的参数,在递归里我们可以声明一个外部变量而储存每次执行的参数。
但是这个是不行的,因为每次递归都需要返回一个function,这意味着你无法返回上一层了。那我们只能把执行的参数当函数的参数传进去了。
直接把自动柯里化给你们(复制可测试)
function curry (fn, length, args) {
length = length || fn.length; //保存函数的参数数量
args = args || [];
return function(){
var _args = args.slice(0),
arg, i;
for(i = 0; i < arguments.length; i++) {//遍历_args,存储每次执行的参数
arg = arguments[i];
_args.push(arg);
}
if(_args.length < length) {
return curry.call(this, fn, length, _args); //递归调用自己
} else {
return fn.apply(this, _args);
}
}
}
可以看到解决return函数个数的是
if(_args.length < length) {
return curry.call(this, fn, length, _args);
}else {
return fn.apply(this, _args);
}
_arg储存了从开始到现在的所有的参数,如果参数”够了“(一个小知识,函数的length==函数参数的个数),就调用fn,把所有参数都传给他,
如果不够,就继续递归,递归的时候呢,把当前的参数传到下一层函数里。
难点就在于每次递归回来都要遍历一下当前的参数,把它push到_args里面。
这样做也避免了空参数的情况,当传入一个空参数,比如这样调用()。_args不变。
大家有空可以研究一下_.curry的实现过程是不是类似呢?-0-
下一个主角,compose,这个函数在fp里面叫组合函数,它能把里面的参数都组合起来,一起调用。类似流式。
function compose(fn1,fn2){
return function(arg){
fn2(fn1(arg));
}
}
var gen= compose(function(b){console.log(b);return b+"be deal with b";} , function(a){console.log(a);return a+"be deal with a";} )
gen("holle"); //hello hello deal with b;
gen就像一个水管,gen("holle");就是往水管里注入了水。
它会从compose左边的函数开始执行,hello是数据流,流过2个函数。第一个函数的返回值会传入到第二个参数继续执行。
在函数式编程里,compose是避免命令式的重要一环,fp要做到的每一个函数都是”纯“的,脏的东西交给使用者。把每个纯的函数组合到compose里面,就像gulp的pipe()一样,每个函数就像一个插件,来处理数据流。
那么我想从右到左处理数据流,
function compose(fn1,fn2){
return function(arg){
fn1(fn2(arg));
}
}
那么和curry一样,我不想只能传2个函数,我想传任意函数怎么办?
这里我们不写自动组合函数了,因为在我分析redux源码的时候发现了这个函数,redux帮我们写了~我们就只来分析一下就好了。
先上一下源码。redux不跟lodash一样一个函数跳来跳去,lodash想只看一部分是不好懂的。redux的compose函数(复制可测试)。
function compose() {
for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
funcs[_key] = arguments[_key]; //把参数都复制到funcs数组里。
} return function () {
if (funcs.length === 0) {
return arguments.length <= 0 ? undefined : arguments[0]; //处理无参数的情况,返回undefined
} var last = funcs[funcs.length - 1]; //这是最后一个处理函数
var rest = funcs.slice(0, -1); //取除了最后一个剩下的处理函数 return rest.reduceRight(function (composed, f) {//每次都执行当前处理函数传入基数
return f(composed);
}, last.apply(undefined, arguments)); //执行最后一个处理函数,返回值作为reduce的基数。
};
}
他的原理很简单,就是把arguments的所有函数都执行一次,把上个函数的返回值传入。这里巧妙的用了数组的reduceRight函数。
你换成reduce也可以,那这个last要换成first,这样就是从左到右执行啦。处理顺序的问题。
它俩是很有用的,例子可以看上篇博文~~点我。
学好这两个函数,我们再打开函数式编程的大门吧~
javascript柯里化及组合函数~的更多相关文章
- JavaScript函数式编程(纯函数、柯里化以及组合函数)
JavaScript函数式编程(纯函数.柯里化以及组合函数) 前言 函数式编程(Functional Programming),又称为泛函编程,是一种编程范式.早在很久以前就提出了函数式编程这个概念了 ...
- 【 js 基础 】【 源码学习 】柯里化和箭头函数
最近在看 redux 的源码,代码结构很简单,主要就是6个文件,其中 index.js 负责将剩余5个文件中定义的方法 export 出来,其他5个文件各自负责一个方法的实现. 大部分代码比较简单,很 ...
- JAVAScript柯里化、部分应用参数终极理解
一.柯里化 在定义柯里化.部分应用参数的概念前,首先必须对闭包有深入的了解和定义,闭包一句话说清楚:函数返回值为函数. 柯里化的定义:将多参函数分解为按步骤接受单个参数的函数,如下代码: var mo ...
- JavaScript柯里化(currying)
参考: https://www.jianshu.com/p/33392cb4b055 https://ruby-china.org/topics/38385 https://stackoverflow ...
- javascript 柯里化
先看一下代码 function add(){ var sum=0; for(var i=0;i<arguments.length;i++){ sum+=arguments[i]; } retur ...
- 函数柯里化实现sum函数
需求 实现sum函数,使其可以传入不定长参数,以及不定次数调用 //示例 console.log(sum(1,2)(3)()) //6 console.log(sum(2,3,4,5)(1,2)(3) ...
- javascript柯里化
function curry(fn){ var slice = Array.prototype.slice; var arr = slice.call(arguments,1); return fun ...
- 柯里化函数之Javascript
柯里化函数之Javascript 定义 依据定义来说,柯里化就是将一个接收"多个"參数的函数拆分成一个或者很多个接收"单一"參数的函数.定义看起来是比較抽象的. ...
- 浅析 JavaScript 中的 函数 currying 柯里化
原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...
随机推荐
- [转帖]迎战AMD 7nm 64核EPYC 英特尔至强也玩起了胶水以及性价比
迎战AMD 7nm 64核EPYC 英特尔至强也玩起了胶水以及性价比 Intel 最强CPU 从最开始的双核 到现在的 28核 发展迅猛. https://www.cnbeta.com/article ...
- Sqlserver tablediff的简单使用
1. 先列举一下自己简单的比较语句 tablediff -sourceserver 10.24.160.73 -sourcedatabase cwbasemi70 -sourceschema lcmi ...
- mysql 中出现:不能打开到主机的连接,在端口3306: 连接失败
由于某种原因,在服务器部署,然后mysql就连接不上了, navicat查看数据库正常,telnet怎么都不同,总会卡一会儿说遗失主机,最后终于找到解决办法 http://www.51testing. ...
- NIO服务器与客户端
这里客户端没有采用NIO形式 服务器: package com.util.Server.NIO; import javax.print.DocFlavor;import java.io.IOExcep ...
- 【转】说说MySQL中的Redo log Undo log都在干啥
阅读目录(Content) 1 undo 1.1 undo是啥 1.2 undo参数 1.3 undo空间管理 2 redo 2.1 redo是啥 2.2 redo 参数 2.3 redo 空间管理 ...
- Centso7 简单优化(阿里云服务器)
##.下载常用包 # yum -y install wget net-tools screen lsof tcpdump nc mtr openssl-devel vim bash-completio ...
- video maker & video tutorials
video maker & video tutorials 视频课程制作工具 https://ke.qq.com/agency/personal/intro.html 成为网络老师 https ...
- 微信小程序支付功能
API:wx.requestPayment() { } https://blog.csdn.net/qishubiao/article/details/80804052
- 十、docker扩展
一.memcached docker pull memcached docker run --name my-memcache -d -p 11211:11211 memcached telnet 1 ...
- spring @Validated 注解开发中使用group分组校验
之前知道spring支持JSR校验,在自己定义的bean中加入@NotNull,@NotBlank,@Length等之类的校验用于处理前台传递过来的request请求,避免在写多余的代码去处理. 但是 ...