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也是以他的名字 ...
随机推荐
- java的数据类型:基本数据类型和引用数据类型
Java数据类型的基本概念 数据类型在计算机语言里面,是对内存位置的一个抽象表达方式,可以理解为针对内存的一种抽象的表达方式. 开始接触每种语言的时候,都会存在对数据类型的认识,有复杂的,有复杂的,各 ...
- laravel创建项目
composer create-project --prefer-dist laravel/laravel=5.5.* blog
- 5 Expressing future time
1 英语中表达将来的时间有四种主要方式:be going to, will, 现在进行时,一般现在时. 2 Make a prediction. 若要预测将来, 可以使用 be going to 或者 ...
- 47.Majority Element I & II
Majority Element I 描述 给定一个整型数组,找出主元素,它在数组中的出现次数严格大于数组元素个数的二分之一. You may assume that the array is non ...
- 【学亮开讲】Oracle内外连接查询20181119
--内连接查询 --需求:查询显示业主编号.业主名称.业主类型名称 select os.id 业主编号,os.name 业主名称,ot.name 业主类型名称 from t_owners os,t_o ...
- word2vec训练&IC分词(待)
参考http://www.52nlp.cn/%E4%B8%AD%E8%8B%B1%E6%96%87%E7%BB%B4%E5%9F%BA%E7%99%BE%E7%A7%91%E8%AF%AD%E6%96 ...
- MVP, MVC, MVVM, 傻傻分不清楚~
1 简介 英文原文:MVC vs. MVP vs. MVVM 三者的目的都是分离关注,使得UI更容易变换(从Winform变为Webform),使得UI更容易进行单元测试. 2 MVC/MVP 2.1 ...
- Js 布尔值操作符
在js中,逻辑与(&&) 和 逻辑或(||)可以对任意的数据类型进行操作,而在高级程序设计中只给出了一系列的规则,并没有进行解释,所以经常记不住.在读其它书籍的时候,读到了它的原理,其 ...
- Linux成为云计算平台的主流操作系统
导读 这是一个人人谈"云"."大数据"的时代,作为一个IT民工,如果与同行间聊天时,不谈及这方面的内容,有人可能会觉得你落伍了,跟不上这个时代了. 这是一个人人 ...
- 使用Google ZXing生成和解析二维码
pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...